我在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都是动态的
答案 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
生成的类可以缓存并在以后重用。
编辑:在单声道上查看此内容,有人可以在VS上验证
答案 1 :(得分:2)
Task.Run(someObj.Execute);
((Action)someObj.Execute)();
似乎编译器总是接受动态类型的(),并且在运行时CLR只看到一层深度的#39;。因此,您可以通过添加显式强制转换或使用Task.Run()隐式执行强制转换来提供帮助。如果这是一个功能或错误!? ......不知道;-) ......