“动态”类型的表达式在运行时的行为是否与同一运行类型时间的非动态表达式相同?

时间:2011-09-12 17:08:19

标签: c# dynamic

考虑以下示例程序:

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#编译器中的错误?

2 个答案:

答案 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));

它隐式地从委托中获取静态签名,并且动态调用在没有任何更多信息的情况下工作。这适用于代表,并作为奖励,动态可调用对象。