奇怪的C#动态行为

7

在研究C#动态关键字的工作原理时,我遇到了一些奇怪的行为。看起来几乎像是一个bug,但更可能是有原因的。

在下面的代码中,有两个调用,一个是对obj1的调用,另一个是对obj2的调用,但只有其中一个能够正确执行。看起来本地变量类型是原因,但"Hello"也应该可以从IDynamicTarget访问,因为它扩展了IDynamicTargetBase。

namespace DynamicTesting
{
    interface IDynamicTargetBase
    {
        string Hello(int a);
    }

    interface IDynamicTarget : IDynamicTargetBase
    {
    }

    class DynamicTarget : IDynamicTarget
    {
        public string Hello(int a)
        {
            return "Hello!";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            dynamic a = 123;

            IDynamicTargetBase obj1 = new DynamicTarget();
            obj1.Hello(a);  // This works just fine

            IDynamicTarget obj2 = new DynamicTarget();
            obj2.Hello(a); // RuntimeBinderException "No overload for method 'Hello' takes '1' arguments"
        }
    }
}

3
似乎在Mono上可以运行:http://ideone.com/PGn3Jp。这是一个在.NET Fiddle上失败的版本:https://dotnetfiddle.net/55ZMAG。 - Kobi
2
这个问题真是个大bug,居然忽视了五年了。。 - Yuval Itzchakov
顺便提一下,这是一个低优先级的错误,因为如果参数是编译时类型化的,那么在这个问题中相同的代码将起作用。我觉得很少有情况需要使用动态参数... - Matías Fidemraizer
1个回答

0

看起来这是一个方法重载解析问题。

只需将 dynamic a = 123 更改为 int a = 123,你的代码就可以工作了。此外,如果你将方法调用更改为 obj2.Hello((int)a);。最后,将变量类型更改为 DynamicTarget 而不是 IDynamicTarget,它也会工作!

为什么?当你使用动态表达式并且有多个方法重载调用具有动态参数时,运行时将无法解析要调用哪个重载,因为方法重载解析基于提供所谓方法调用时的参数的类型和顺序。

我猜测,当一个接口还实现其他接口时,运行时重载解析会失败,并且运行时似乎理解第二个接口不保证定义其他接口中也实现的重载,因此它强制你在编译时提供参数的实际类型。

[...] 但是 "Hello" 也应该从 IDynamicTarget 中访问, 因为它扩展了 IDynamicTargetBase。

它是可访问的,但运行时无法解析如何提供方法的参数...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接