c#动态的不可预测的行为 - 代码日志

c#动态的不可预测的行为

在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 };
}

注意:typeof both exectuer和someObj都是动态的

我们来看下面的代码:

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();

    }
}

动态使用反射来访问对象方法和字段,并且由于它不能知道确切的类型,它必须依赖于在其上运行的对象中存在的类型信息。

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

使用时

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

创建匿名类,您创建类型为对象的字段x的类(动态是幕后的对象)。当你调用obj.x时,动态看到这个字段类型是一个对象,并且不会检查这个字段指向什么样的类型。而且由于对象没有像代理这样的Invoke()方法,它会抛出异常。如果将方法参数类型更改为Action,它将会工作。

我猜这个决定是为了提供更好的表现,来检查字段类型而不是字段包含的类型。换句话说,当您检查字段类型由动态生成的CallSite类可以被缓存并稍后重用。

参考文献:
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上验证

http://stackoverflow.com/questions/38114762/unpredictible-behaviour-in-c-sharp-dynamic

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:c#动态的不可预测的行为