代码之家  ›  专栏  ›  技术社区  ›  nvoigt

为什么在尝试使用动态参数调用扩展方法时出现错误CS1973

  •  3
  • nvoigt  · 技术社区  · 7 年前

    考虑以下代码:

    internal static class Program
    {
        public static string ExtensionMethod(this string format, dynamic args)
        {
            return format + args.ToString();
        }
    
        private static void Main()
        {
            string test = "hello ";
            dynamic d = new { World = "world" };
    
            // Error CS1973  'string' has no applicable method named 'ExtensionMethod'
            //                but appears to have an extension method by that name. 
            //               Extension methods cannot be dynamically dispatched. 
            //               Consider casting the dynamic arguments or calling
            //               the extension method without the extension method syntax.
            var result = test.ExtensionMethod(d);
    
            // this syntax works fine
            var result2 = Program.ExtensionMethod(test, d);
    
            // for whatever reason, this works, too!
            var result3 = test.ExtensionMethod((object)d);
    
            // even this works...
            var result4 = test.ExtensionMethod(new { World = "world" });
    
            Console.WriteLine(result);
            Console.ReadLine();
        }
    }
    

    我不明白这背后的逻辑。我确实明白 第一 扩展方法的参数不能是动态的。我甚至可以理解,如果我通过了什么东西,调度就不起作用了 但是 另一种动态。但显然是这样。它可以将其他类型映射到动态。但当我通过 精确类型 方法是否需要?它无法映射 dynamic 动态 ? 这对我来说毫无意义。

    我可以阅读并理解错误,我显然知道解决方法,但有人能启发我吗 为什么? 是否存在此错误?我看不到的更深层次的问题是什么?


    有几个现存的问题可以解释为什么第一个论点( string )在这种情况下,不能是动态的- How to call an extension method of a dynamic type? 显示将分机呼叫转换为常规呼叫的解决方法。事实上,这在我的情况下是有效的。

    还有很多“什么是CS1973”的问题( https://stackoverflow.com/search?q=CS1973 )但他们中的大多数人都是先处理( this Xxxx )参数为 动态 (这听起来很失败)或只是表现出同样的行为,而没有解释为什么喜欢 'System.Web.Mvc.HtmlHelper' has no applicable method named 'Partial' (调用强类型对象上的已知扩展,但传递 动态 作为第二个参数)。

    但这并不能解释为什么在编译时确定单个扩展方法时仍然不能使用,这就是这个问题所在。

    1 回复  |  直到 7 年前
        1
  •  6
  •   D Stanley    7 年前

    因为扩展方法绑定在 编译时间 . 它们基本上由编译器转换为静态方法调用,这意味着

    var result = test.ExtensionMethod(d);
    

    由编译器转换为

    var result = Program.ExtensionMethod(test, d);
    

    什么时候 任何 参数包括 dynamic 然后 全部的 绑定延迟到运行时。 运行时绑定器当前不支持编译器知道的扩展方法的绑定,并生成错误。

    我甚至可以理解,如果我只传递了另一个动态,调度是否会不起作用。

    记住这一点 动态 (如 var )不是 类型 . 它只意味着“我不知道在编译时这是什么类型-我将让动态运行时查看 真实的 然后键入并确定如何处理它。

    所以当你说:

    dynamic d = new { World = "world" };
    

    d 不是“a” 动态 “。在运行时 d 将是匿名类型。

    能够 动态粘结剂支架扩展方法?可能吧,但到目前为止 * ,与设计、实现、测试、发布和支持此类功能的成本相比,附加值并不值得。如果您觉得这将是对框架的一个有价值的补充,那么可以在 http://connect.microsoft.com/ .

    我还要指出,没有必要使用 动态 在您的(尽管是人为的)场景中。如果使用 object 而不是 动态 (并将扩展方法移动到静态类):

    public static string ExtensionMethod(this string format, object args)
    {
        return format + args.ToString();
    }
    private static void Main()
    {
        string test = "hello ";
        object d = new { World = "world" };
    
        // Error CS1973  'string' has no applicable method named 'ExtensionMethod'
        //                but appears to have an extension method by that name. 
        //               Extension methods cannot be dynamically dispatched. 
        //               Consider casting the dynamic arguments or calling
        //               the extension method without the extension method syntax.
        var result = test.ExtensionMethod(d);
    
        // this syntax works fine
        var result2 = Program.ExtensionMethod(test, d);
    
        // for whatever reason, this works, too!
        var result3 = test.ExtensionMethod((object)d);
    
        // even this works...
        var result4 = test.ExtensionMethod(new { World = "world" });
    
        Console.WriteLine(result);
        Console.ReadLine();
    }
    

    * 至少在2011年,在这种情况下,找到调用方法的问题被认为太难了——参见Eric Lippert的答案- https://stackoverflow.com/a/5313149/477420 : " ...这意味着,为了正确解析动态扩展方法调用,DLR必须在运行时知道源代码中的所有命名空间嵌套和使用指令。我们没有一种方便的机制将所有这些信息编码到呼叫站点中。。。"