C# 在解析动态对象时是否为 var 选择了错误的类型?

发布于 2025-01-08 11:28:56 字数 682 浏览 6 评论 0原文

我正在使用以下代码将一些 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

揪着可爱 2025-01-15 11:28:56

..但编译器似乎做出了“错误的猜测”。谁能给我解释一下吗?

当您使用dynamic时,整个表达式在编译时被视为动态表达式,这会导致编译器将所有内容视为动态并获取运行时绑定。

C# 语言规范 7.2 对此进行了解释:

当不涉及动态表达式时,C#默认为静态绑定,这意味着在选择过程中使用组成表达式的编译时类型。但是,当上面列出的操作中的组成表达式之一是动态表达式时,该操作将被动态绑定。

这基本上意味着大多数具有声明为 dynamic 元素的操作(类型在规范第 7.2 节中列出)将被评估为 dynamic,并且结果将是动态

在您的情况下,此语句:

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

使用动态,因此,它被视为动态表达式。由于“方法调用”是受绑定约束的 C# 操作之一 (7.2),因此编译器将其视为动态绑定,这会导致其计算结果为:

dynamic settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

反过来,这会导致 DateTime.Parse表达式是动态绑定的,这反过来又使它们返回动态

当您执行 DateTime startDate = DateTime.Parse(settings.startDate); 时,您的“修复”会起作用,因为这会强制对以下结果进行隐式动态转换(规范第 6.1.8 节中所述) DateTime.Parse 方法转换为 DateTime:

存在从 Dynamic 类型的表达式到任何类型 T 的隐式动态转换。该转换是动态绑定的(第 7.2.2 节),这意味着将在运行时从运行时类型寻求隐式转换表达式到T的转换。如果没有找到转换,则抛出运行时异常。

在这种情况下,转换是有效的,因此从那时起您可以有效地将所有内容切换回静态绑定。

..but it seems like the compiler is making a 'bad guess'. Can anyone explain this to me?

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:

When no dynamic expressions are involved, C# defaults to static binding, which means that the compile-time types of constituent expressions are used in the selection process. However, when one of the constituent expressions in the operations listed above is a dynamic expression, the operation is instead dynamically bound.

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 as dynamic, and the result will be a dynamic.

In your case, this statement:

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

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:

dynamic settings = new JavaScriptSerializer().Deserialize<dynamic>(json);

This, in turn, causes the DateTime.Parse expressions to be dynamic bound, which in turn makes them return dynamic.

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:

An implicit dynamic conversion exists from an expression of type dynamic to any type T. The conversion is dynamically bound (§7.2.2), which means that an implicit conversion will be sought at run-time from the run-time type of the expression to T. If no conversion is found, a run-time exception is thrown.

In this case, the conversion is valid, so you effectively switch everything back to static binding from then on.

药祭#氼 2025-01-15 11:28:56

我认为这并不特别令人惊讶。

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.

趴在窗边数星星i 2025-01-15 11:28:56

这是根据规范。参见第 7.6.5 节:

如果至少满足以下条件之一,则调用表达式是动态绑定的(第 7.2.2 节):

主表达式具有编译时类型动态

• 可选参数列表中至少有一个参数具有编译时类型dynamic,并且主表达式没有委托类型。

考虑这种情况:

class Foo {
    public int M(string s) { return 0; }
    public string M(int s) { return String.Empty; }
}

Foo foo = new Foo();
dynamic d = // something dynamic
var m = foo.M(d);

m 的编译时类型应该是什么?编译器无法判断,因为直到运行时它才知道调用了 Foo.M 的哪个重载。因此,它表示 m 是动态的。

现在,您可以说它应该能够弄清楚 DateTime.Parse 只有一个重载,即使它没有,但它的所有重载都具有相同的返回类型,在这种情况下它应该能够找出编译时类型。这是一个公平的观点,而且可能是最好的埃里克·利珀特。我问了一个单独的问题来深入了解这一点:为什么即使只有一种可能的返回类型,方法调用表达式也具有动态类型?

This is per the spec. See §7.6.5:

An invocation-expression is dynamically bound (§7.2.2) if at least one of the following holds:

• The primary-expression has compile-time type dynamic.

• At least one argument of the optional argument-list has compile-time type dynamic and the primary-expression does not have a delegate type.

Consider this scenario:

class Foo {
    public int M(string s) { return 0; }
    public string M(int s) { return String.Empty; }
}

Foo foo = new Foo();
dynamic d = // something dynamic
var m = foo.M(d);

What should be the compile-time type of m? The compiler can't tell, because it won't know until runtime which overload of Foo.M is invoked. Thus, it says m 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?.

被你宠の有点坏 2025-01-15 11:28:56

这里有两个不同的概念

  1. 动态:这是在运行时解析的任何类型
  2. var:这是执行的隐式静态类型在编译时

因此,如果您

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);

在编译时这样做,它将解析为动态类型,而在运行时它将解析为具体类型。编译器检查正确的部分 new JavaScriptSerializer().Deserialize(json);,它返回动态。在编译时,这指示编译器放弃所有类型安全检查并将其保留到运行时。

该代码

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
DateTime startDate = DateTime.Parse(settings.startDate);

明确表示动态对象是具体类型,因此编译器能够推断该类型及其所有方法,并且能够在编译时执行静态类型检查。

There are two different concepts here

  1. dynamic: which is any type that is resolved at run-time
  2. var: which is implicit static typing that is performed at compile time

So if you do

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
var startDate = DateTime.Parse(settings.startDate);

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

var settings = new JavaScriptSerializer().Deserialize<dynamic>(json);
DateTime startDate = DateTime.Parse(settings.startDate);

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文