【问题标题】:C# dynamic type causes Console.WriteLine to be resolved with reflection in ILC# 动态类型导致 Console.WriteLine 在 IL 中通过反射解决
【发布时间】:2012-09-20 19:39:48
【问题描述】:

我一直在尝试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 的调用似乎是通过内部反射执行的。

IL_0072:  ldstr       "WriteLine"
....
IL_00BF:  ldtoken     System.Console

在第一段代码中,属性被检索和存储后,是一行IL语句,调用了System.Console.WriteLine

那么为什么dynamic 类型的调用需要所有这些额外的功能?

【问题讨论】:

    标签: c# dynamic reflection compiler-construction il


    【解决方案1】:

    因为变量是dynamic,所以在编译时无法知道应该调用WriteLine 的哪个重载。直到运行时我们才知道dynamic 对象的实际类型。由于dynamic 的工作方式,重要的是它不仅在编译时被视为object;部分功能是它在运行时确定正确的过载。

    如果您将对象转换为非动态对象(即在调用 ToString 或返回到 ExpandoObject 之后将对象转换为 string),然后将其传递给 WriteLine,那么您应该会看到反射调用消失并查看它在编译时静态确定WriteLine 的正确重载。

    【讨论】:

    • 实际上,你的ToString() 技巧是行不通的(除非你还向string 添加显式转换)。
    【解决方案2】:

    正在发生的事情是编译器正在以一种可以“后期绑定”的方式创建您的代码。后期绑定意味着与传统数据类型和对象一样,不是在编译期间解析对象,而是在运行时解析对象,而您的程序集实际上在内存中并正在运行。

    如果您在 Reflector 或 dotPeek 中查看您的代码,您会看到您的动态对象被 [Dynamic] 属性修饰。当您的程序在内存中运行时,当涉及到使用此属性修饰的对象时,对该对象的调用通过动态Container(或您的对象被调用的任何内容)进行管道传输。这个Container 使用负责运行时绑定的Binder 进行初始化。这就是所有对Microsoft.CSharp.RuntimeBinder 的调用所做的事情。这个RuntimeBinder 稍后用于调用属性或方法或任何动态的东西。

    我希望这能让事情变得更清楚。我在我的安卓上打字,所以解释可能不太理想。我稍后会清理它。

    【讨论】:

    • 在这种情况下你不会看到任何属性,因为局部变量不能有属性。
    • @svick -- 不,但属性可以具有属性,在他的示例中,Name 是一个属性
    • 但是该属性的类型是string,而不是objectDynamicAttribute
    • 哦,你是对的....Name 属性是字符串。但是Container 对象将使用[dynamic] 属性标记进行修饰。
    • 不,不会。对象没有属性,本地人也没有。仅当您从外部访问 dynamic 对象时才需要该属性,如果它是局部变量则不会发生这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2014-08-24
    • 2017-09-26
    • 1970-01-01
    相关资源
    最近更新 更多