静态和动态C#之间的互操作性差

Vag*_*lov 6 c# static dynamic c#-4.0

这个问题在某种程度上是对相关帖子的说明,我相信下面的例子描述了问题的本质.

class Program
{
    public static IList<string> GetData(string arg)
    {
        return new string[] {"a", "b", "c"};
    }

    static void Main(string[] args)
    {
        var arg1 = "abc";
        var res1 = GetData(arg1);
        Console.WriteLine(res1.Count());

        dynamic arg2 = "abc";
        var res2 = GetData(arg2);
        try
        {
            Console.WriteLine(res2.Count());
        }
        catch (RuntimeBinderException)
        {
            Console.WriteLine("Exception when accessing Count method");
        }

        IEnumerable<string> res3 = res2;
        Console.WriteLine(res3.Count());
    }
}
Run Code Online (Sandbox Code Playgroud)

第二次调用GetData引发异常只是因为GetData接收到一个转换为动态的参数,这不是很糟糕吗?这个方法本身很好用这个参数:它将它视为一个字符串并返回正确的结果.但结果再次被转换为动态,突然结果数据无法根据其基础类型进行处理.除非它明确地转换回静态类型,正如我们在示例的最后几行中看到的那样.

我不明白为什么必须以这种方式实施.它打破了静态和动态类型之间的互操作性.一旦使用了动态,它会感染调用链的其余部分,从而可能导致像这样的问题.

更新.有些人指出Count()是一种扩展方法,有意义的是它不被识别.然后我将res2.Count()调用更改为res2.Count(从扩展方法更改为Ilist的属性),但程序在同一个地方引发了相同的异常!现在这很奇怪.

UPDATE2.flq指出了Eric Lippert关于这个主题的博客文章,我相信这篇文章给出了为什么以这种方式实现的充分理由:http: //blogs.msdn.com/b/ericlippert/archive/2012/10/22/a -method基团的-one.aspx

Dan*_*ant 7

问题是Count是一种扩展方法.

您如何在运行时找到扩展方法?"范围内"的信息基于正在编译的特定文件中的"using"语句.但是,这些在运行时未包含在已编译的代码中.它应该在所有加载的程序集中查看所有可能的扩展方法吗?那些项目引用但尚未加载的程序集呢?如果您尝试允许动态使用扩展方法,则会出现数量惊人的边界情况.

在这种情况下,正确的解决方案是以非扩展形式调用静态方法:

Enumerable.Count(res2)

或者,因为您知道IList<T>在这种情况下,只需使用Count属性:

res2.Count <---编辑:这不起作用,因为它是由数组实现时显式实现的接口属性.


再看一下你的问题,我看到真正的问题不是关于扩展方法解决方案本身,而是为什么它无法确定是否存在可能的单一方法解析,因此静态地知道类型.我将不得不进一步思考,但我猜这是一个类似的边界情况问题,特别是一旦你开始考虑多次重载.


这是一个令人讨厌的边界情况,可能会出现在一般情况下(虽然不是直接适用于您的情况,因为您派生自Object).

假设你在程序集A中有一个类Base.在程序集B中还有一个类Derived:Base.在Derived类中,你有上面的代码,你认为GetData只有一个可能的解决方案.但是,现在假设发布了一个新版本的程序集A,它具有带有不同签名的受保护GetData方法.您的派生类继承了这一点,DLR尽职尽责地允许动态绑定到这个新方法.突然,返回类型可能不是您所假设的.请注意,所有这些都可以在不重新编译程序集B的情况发生.这意味着运行前编译器不能假定DLR将解析为运行时编译器认为是唯一选项的类型,因为运行时的动态环境可能产生不同的类型.