考虑以下示例程序:
using System;
public delegate string MyDelegateType(int integer);
partial class Program
{
static string MyMethod(int integer) { return integer.ToString(); }
static void Main()
{
Func<int, string> func = MyMethod;
// Scenario 1: works
var newDelegate1 = new MyDelegateType(func);
newDelegate1(47);
// Scenario 2: doesn’t work
dynamic dyn = func;
var newDelegate2 = new MyDelegateType(dyn);
newDelegate2(47);
}
}
第一个按预期工作 - 转换为MyDelegateType
成功。但是,第二个会抛出RuntimeBinderException
,并显示错误消息:
无法隐式转换类型'System.Func&lt; int,string&gt;'到'MyDelegateType'
C#规范中是否有允许此行为的内容,或者这是Microsoft C#编译器中的错误?
答案 0 :(得分:16)
好抓Timwi。
我们对动态方法组的支持很弱。例如,考虑这个更简单的情况:
class C
{
public void M() {}
}
class P
{
static void Main()
{
dynamic d = new C();
C c = new C();
Action a1 = c.M; // works
Action a2 = d.M; // fails at runtime
动态运行时将d.M
解释为属性get (或字段访问),当它作为方法组解析时,它会在运行时失败。
同样的事情发生在你的情况下,它只是有点模糊。当你说MyDelegate x = new MyDelegate(someOtherDelegate);
被编译器处理时就像你说MyDelegate x = someOtherDelegate.Invoke;
一样。动态运行时块不知道要进行那种转换,即使这样做,它也无法处理解析方法组,该方法组是表达式.Invoke
部分的结果。
C#规范中是否有允许此行为的内容,或者这是Microsoft C#编译器中的错误?
规范没有提到这应该是运行时错误,并且暗示它应该在运行时正确处理;显然,实施不会这样做。虽然这是实施的一个缺点,但我不会称之为“错误”,因为我们故意制造了你发现的行为。我们没有资源让这些表达式完全正确,所以我们将它们留作错误。如果我们在动态运行时中获得表示方法组的好方法,我们可能会实现它。
类似地,动态代码中没有办法表示“这个动态的东西是一个lambda表达式,其中参数的类型将在运行时确定”。如果我们有一个很好的方式代表未来的人,我们可能会做这项工作。
Sam在2008年谈到了这个问题;看到他的文章:http://blogs.msdn.com/b/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx
答案 1 :(得分:2)
我也遇到了这个限制。虽然我无法回答为什么比Eric Lippert更好,但有一个直接的解决方法。
var newDelegate2 = new MyDelegateType(x => dyn(x));
它隐式地从委托中获取静态签名,并且动态调用在没有任何更多信息的情况下工作。这适用于代表,并作为奖励,动态可调用对象。