C# 在解析动态对象时是否为 var 选择了错误的类型?
我正在使用以下代码将一些 Json 转换为动态对象。当我在动态类型的属性上使用 DateTime.Parse 时,我希望 var 猜测它的类型是 DateTime...相反,它保持动态类型。这不对吧?
完整示例如下。
var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);
var endDate = DateTime.Parse(settings.endDate);
var userId = int.Parse(settings.userId);
startDate、endDate 和 userId 仍然是动态的,这意味着我无法在以后的 Lambda 表达式中使用它们。显然,我可以用以下方法修复代码:
DateTime startDate = DateTime.Parse(settings.startDate);
DateTime endDate = DateTime.Parse(settings.endDate);
int userId = int.Parse(settings.userId);
..但编译器似乎做出了“错误的猜测”。谁能向我解释一下吗?
谢谢
I am using the following code to convert some Json into a dynamic object. When I use DateTime.Parse on a property of my dynamic type I would expect the var to guess that it's type is a DateTime... instead, it stays as a dynamic. This can't be right, can it?
Full example below.
var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);
var endDate = DateTime.Parse(settings.endDate);
var userId = int.Parse(settings.userId);
startDate, endDate and userId are all still dynamic, which means I then cannot use them in a later Lambda expressions. Obviously, I can fix the code with:
DateTime startDate = DateTime.Parse(settings.startDate);
DateTime endDate = DateTime.Parse(settings.endDate);
int userId = int.Parse(settings.userId);
..but it seems like the compiler is making a 'bad guess'. Can anyone explain this to me?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您使用
dynamic
时,整个表达式在编译时被视为动态表达式,这会导致编译器将所有内容视为动态并获取运行时绑定。C# 语言规范 7.2 对此进行了解释:
这基本上意味着大多数具有声明为
dynamic
元素的操作(类型在规范第 7.2 节中列出)将被评估为dynamic
,并且结果将是动态
。在您的情况下,此语句:
使用动态,因此,它被视为动态表达式。由于“方法调用”是受绑定约束的 C# 操作之一 (7.2),因此编译器将其视为动态绑定,这会导致其计算结果为:
反过来,这会导致
DateTime.Parse
表达式是动态绑定的,这反过来又使它们返回动态
。当您执行
DateTime startDate = DateTime.Parse(settings.startDate);
时,您的“修复”会起作用,因为这会强制对以下结果进行隐式动态转换(规范第 6.1.8 节中所述) DateTime.Parse 方法转换为 DateTime:在这种情况下,转换是有效的,因此从那时起您可以有效地将所有内容切换回静态绑定。
When you use
dynamic
, the entire expression is treated at compile time as a dynamic expression, which causes the compiler to treat everything as dynamic and get run-time binding.This is explained in 7.2 of the C# Language specification:
This basically means that most operations (the types are listed in section 7.2 of the spec) which have any element that is declared as
dynamic
will be evaluated asdynamic
, and the result will be adynamic
.In your case, this statement:
Uses dynamic, so, it getst reated as a dynamic expression. Since "Method invocation" is one of the C# operations subject to binding (7.2), the compiler treats this as dynamic bound, which causes this to evaluate to:
This, in turn, causes the
DateTime.Parse
expressions to be dynamic bound, which in turn makes them returndynamic
.Your "fix" works when you do
DateTime startDate = DateTime.Parse(settings.startDate);
because this forces an implicit dynamic conversion (described in section 6.1.8 of the spec) of the result of the DateTime.Parse method to a DateTime:In this case, the conversion is valid, so you effectively switch everything back to static binding from then on.
我认为这并不特别令人惊讶。
DateTime.Parse()
将计算为动态。DateTime startDate =
执行从动态到 DateTime 的运行时分配。你刚刚将两者结合起来。
编译器不会猜测
DateTime.Parse()
的类型不是动态的,但它足够聪明地意识到,如果您将此值分配给 DateTime然后假设成功,您将得到一个日期时间。I don't think this is particularly surprising.
DateTime.Parse(<dynamic>)
will evaluate to dynamic.DateTime startDate = <dynamic>
does a runtime assignment from a dynamic to a DateTime.You've just combined the two.
The compiler isn't guessing the type of
DateTime.Parse(<dynamic>)
to be anything other than dynamic, but it's clever enough to realise that if you do an assignment of this value to a DateTime then assuming it is successful you're left with a DateTime.这是根据规范。参见第 7.6.5 节:
考虑这种情况:
m
的编译时类型应该是什么?编译器无法判断,因为直到运行时它才知道调用了Foo.M
的哪个重载。因此,它表示m
是动态的。现在,您可以说它应该能够弄清楚
DateTime.Parse
只有一个重载,即使它没有,但它的所有重载都具有相同的返回类型,在这种情况下它应该能够找出编译时类型。这是一个公平的观点,而且可能是最好的埃里克·利珀特。我问了一个单独的问题来深入了解这一点:为什么即使只有一种可能的返回类型,方法调用表达式也具有动态类型? 。This is per the spec. See §7.6.5:
Consider this scenario:
What should be the compile-time type of
m
? The compiler can't tell, because it won't know until runtime which overload ofFoo.M
is invoked. Thus, it saysm
is dynamic.Now, you could say that it should be able to figure out
DateTime.Parse
has only one overload, and even if it didn't but all of its overloads have the same return type that in that case it should be able to figure out the compile-time type. That would be a fair point, and is probably best Eric Lippert. I asked a separate question to get insight into this: Why does a method invocation expression have type dynamic even when there is only one possible return type?.这里有两个不同的概念
因此,如果您
在编译时这样做,它将解析为动态类型,而在运行时它将解析为具体类型。编译器检查正确的部分
new JavaScriptSerializer().Deserialize(json);
,它返回动态。在编译时,这指示编译器放弃所有类型安全检查并将其保留到运行时。该代码
明确表示动态对象是具体类型,因此编译器能够推断该类型及其所有方法,并且能够在编译时执行静态类型检查。
There are two different concepts here
So if you do
at compile time it is resolved to dynamic type and at run time it is resolved to a concrete type. Compiler checks the right part
new JavaScriptSerializer().Deserialize<dynamic>(json);
, which returns dynamic. At compile time this instructs compiler to drop all type-safty checks and live it till run time.This code
explicitly says that the dynamic object is of concrete type, therefore compiler is able to infer the type, all its methods and is able to perform static type check at compile time.