我一直在研究 LINQPad 中的一些 C# 语句以了解发出了什么中间语言代码。
我首先尝试了以下代码:
var Container = new {Name = "James"};
Console.WriteLine(Container.Name);
然后看到发出了以下六行 IL:
IL_0001: ldstr "James"
IL_0006: newobj <>f__AnonymousType0<System.String>..ctor
IL_000B: stloc.0
IL_000C: ldloc.0
IL_000D: callvirt <>f__AnonymousType0<System.String>.get_Name
IL_0012: call System.Console.WriteLine
这大致符合我的预期,并且很好地展示了匿名类型是如何只读/不可变的,因为没有 set_Name 属性。
接下来我尝试了语句:
dynamic Container = new System.Dynamic.ExpandoObject();
Container.Name = "James";
Console.WriteLine(Container.Name);
这导致大量 IL 被释放。我不会在这里粘贴,但您可以在 this pastebin 中找到它.
我知道在管理动态类型和 ExpandoObject 方面有相当多的开销,但我不明白为什么对 System.Console.WriteLine
的调用似乎在这里通过内部反射(reflection)进行的案例。
IL_0072: ldstr "WriteLine"
....
IL_00BF: ldtoken System.Console
在第一段代码中,在检索并存储属性之后,它是一条调用 System.Console.WriteLine
的单行 IL 语句。
那么为什么使用 dynamic
类型的调用需要所有这些额外的东西?
最佳答案
因为变量是动态
,所以在编译时无法知道应该调用WriteLine
的哪个重载。直到运行时我们才知道 dynamic
对象的实际类型。由于 dynamic
的工作方式,重要的是它不仅在编译时被视为一个 object
;部分功能在于它在运行时确定正确的过载。
如果您将对象转换为非动态对象(即调用 ToString
后的 string
或返回 ExpandoObject
)然后将其传递到 WriteLine
然后你应该看到反射调用消失并看到它在编译时静态地确定 WriteLine
的正确重载。
关于C# 动态类型导致 Console.WriteLine 在 IL 中通过反射解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12519738/