且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为什么是“动态"?当用作泛型类型参数时,对于所有类型都不是协变和逆变的?

更新时间:2023-02-05 21:06:16

我想知道当用作泛型类型参数时,动态是否在语义上等同于对象.

I am wondering if dynamic is semantically equivalent to object when used as a generic type parameter.

你的猜想完全正确.

作为类型的动态"只不过是带有有趣帽子的对象",这顶帽子说与其对类型对象的这个表达式进行静态类型检查,不如生成在运行时进行类型检查的代码".在所有其他方面,动态只是对象,故事的结尾.

"dynamic" as a type is nothing more than "object" with a funny hat on, a hat that says "rather than doing static type checking for this expression of type object, generate code that does the type checking at runtime". In all other respects, dynamic is just object, end of story.

我很好奇为什么存在这种限制,因为在为变量或形式参数赋值时两者是不同的.

I am curious why this limitation exists since the two are different when assigning values to variables or formal parameters.

先从编译器的角度考虑,然后再从 IL 验证器的角度考虑.

Think about it from the compiler's perspective and then from the IL verifier's perspective.

当你给一个变量赋值时,编译器基本上会说我需要生成代码,将这样一个类型的值隐式转换为变量的确切类型".编译器生成执行此操作的代码,IL 验证器验证其正确性.

When you're assigning a value to a variable, the compiler basically says "I need to generate code that does an implicit conversion from a value of such and such a type to the exact type of the variable". The compiler generates code that does that, and the IL verifier verifies its correctness.

即编译器生成:

Frob x = (Frob)whatever;

但将转换限制为隐式转换,而不是显式转换.

But limits the conversions to implicit conversions, not explicit conversions.

当值是动态的时,编译器基本上会说我需要生成代码,在运行时询问这个对象,确定它的类型,再次启动编译器,并吐出一小块 IL 来转换这个对象是什么到该变量的类型,运行该代码,并将结果分配给该变量.如果其中任何一个失败,则抛出."

When the value is dynamic, the compiler basically says "I need to generate code that interrogates this object at runtime, determines its type, starts up the compiler again, and spits out a small chunk of IL that converts whatever this object is to the type of this variable, runs that code, and assigns the result to this variable. And if any of that fails, throw."

也就是说,编译器生成以下等价物:

That is, the compiler generates the moral equivalent of:

Frob x = MakeMeAConversionFunctionAtRuntime<Frob>((object)whatever);

验证者甚至不会眨眼.验证器看到一个返回 Frob 的方法.如果该方法无法将任何"转换为 Frob,则该方法可能会抛出异常;不管怎样,只有一个 Frob 被写入 x.

The verifier doesn't even blink at that. The verifier sees a method that returns a Frob. That method might throw an exception if it is unable to turn "whatever" into a Frob; either way, nothing but a Frob ever gets written into x.

现在想想你的协方差情况.从 CLR 的角度来看,没有动态"这样的东西.只要有动态"类型参数,编译器就会简单地生成对象"作为类型参数.动态"是 C# 语言功能,而不是公共语言运行时功能.如果对象"的协变或逆变不合法,那么动态"也不合法.编译器无法生成 IL 以使 CLR 的类型系统以不同方式工作.

Now think about your covariance situation. From the CLR's perspective, there is no such thing as "dynamic". Everywhere that you have a type argument that is "dynamic", the compiler simply generates "object" as a type argument. "dynamic" is a C# language feature, not a Common Language Runtime feature. If covariance or contravariance on "object" isn't legal, then it isn't legal on "dynamic" either. There's no IL that the compiler can generate to make the CLR's type system work differently.

这就解释了为什么你观察到有一个从 ListList 的转换;编译器知道它们是相同的类型.规范实际上指出这两种类型在它们之间具有身份转换;它们是相同的类型.

This then explains why it is that you observe that there is a conversion from, say, List<dynamic> to and from List<object>; the compiler knows that they are the same type. The specification actually calls out that these two types have an identity conversion between them; they are identical types.

这一切都有意义吗?你似乎对动态背后的设计原则很感兴趣;与其尝试自己从基本原理和实验中推导出它们,您还可以省去麻烦并阅读 Chris Burrows 关于该主题的博客文章.他完成了大部分的实现和相当数量的功能设计.

Does that all make sense? You seem very interested in the design principles that underly dynamic; rather than trying to deduce them from first principles and experiments yourself, you could save yourself the bother and read Chris Burrows' blog articles on the subject. He did most of the implementation and a fair amount of the design of the feature.