c#dynamic中不可预测的行为

时间:2016-06-30 05:39:52

标签: c# dynamic

我在C#学习动态期间发现了一个错误(功能?)。任何人都可以解释我,为什么我有例外?

static class Program
{
    public static void Main(string[] args)
    {
        dynamic someObj = ConstructSomeObj((Action)(() => Console.WriteLine("wtf")));

        var executer = someObj.Execute;
        executer();         // shows "wtf"
        someObj.Execute();  // throws RuntimeBinderException 

        Console.ReadKey();
    }

    static dynamic ConstructSomeObj(dynamic param) 
        => new { Execute = param };
}

注意:exectuer和someObj的type都是动态的

2 个答案:

答案 0 :(得分:11)

让我们看看以下代码:

using System;
using System.Collections.Generic;


public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("first");

        // works perfectly!!!
        dynamic foo = new { x=(Action)(() => Console.WriteLine("ok")) };
        foo.x();

        // fails
        dynamic foo2 = new { x=(object)(Action)(() => Console.WriteLine("ok2")) };
        foo2.x();

    }
}

dynamic使用反射来访问对象的方法和字段,因为它无法知道确切的类型,所以它必须依赖于操作对象的类型信息。

当匿名类型中的字段x被正确输入为委托调用foo.x()时,因为动态可以看到该字段值是委托。

使用时

static dynamic ConstructSomeObj(dynamic param) 
    { return new { x = param }; }

创建一个匿名类,您创建了类型为x的字段object的类(幕后动态为object)。当您致电obj.x时,动态会看到该字段类型为object,并且无需检查此字段指向的确切类型。并且由于对象没有像委托一样的Invoke()方法,因此抛出异常。如果您将方法参数类型更改为Action,它将起作用。

我猜这个决定检查字段类型而不是字段包含的值的类型,以提供更好的性能。换句话说,当您检查字段类型CallSite时,dynamic生成的类可以缓存并在以后重用。

参考文献: https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/Binder.cs

https://github.com/mono/mono/blob/ef407901f8fdd9ed8c377dbec8123b5afb932ebb/mcs/class/Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpInvokeMemberBinder.cs

编辑:在单声道上查看此内容,有人可以在VS上验证

答案 1 :(得分:2)

好的,有趣的。这两行将起作用:

Task.Run(someObj.Execute);  
((Action)someObj.Execute)();

似乎编译器总是接受动态类型的(),并且在运行时CLR只看到一层深度的#39;。因此,您可以通过添加显式强制转换或使用Task.Run()隐式执行强制转换来提供帮助。如果这是一个功能或错误!? ......不知道;-) ......