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

nvo*_*igt 3 c# extension-methods dynamic dispatch

请考虑以下代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不明白这背后的逻辑.我明白,扩展方法的第一个参数不能是动态的.而且我甚至会理解,如果我传递了一些东西而不是另一种动态,那么调度是否会起作用.但显然确实如此.它可以将其他类型映射到动态.但是当我传递方法所需的确切类型时,调度不起作用?它无法映射dynamicdynamic?这对我来说没有意义.

我可以阅读并理解错误,我显然知道解决方法,但有人可以告诉我为什么会出现这个错误吗?什么是我看不到的更深层次的问题?


有几个现有的问题解释为什么string在这种情况下第一个参数()不能是动态的 - 如何调用动态类型的扩展方法?显示解决方法以将分机呼叫转换为常规呼叫.事实上它适用于我的情况.

还有很多"什么是CS1973"的问题(https://stackoverflow.com/search?q=CS1973)但是他们大多数都处理了first(this Xxxx)参数dynamic(这听起来很公平)或者只是显示相同的行为没有解释为什么像'System.Web.Mvc.HtmlHelper'没有名为'Partial'的适用方法(在强类型对象上调用已知扩展名但dynamic作为第二个参数传递).

但这并不能解释为什么在编译时可以确定单个扩展方法仍然无法使用这就是这个问题的含义.

D S*_*ley 5

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

var result = test.ExtensionMethod(d);
Run Code Online (Sandbox Code Playgroud)

由编译器转换为

var result = Program.ExtensionMethod(test, d);
Run Code Online (Sandbox Code Playgroud)

任何参数为时dynamic,则所有绑定都将延迟到运行时.运行时绑定程序当前不支持编译器知道的扩展方法的绑定,并生成错误.

而且我甚至会理解,如果我传递了一些东西而不是另一种动态,那么调度是否会起作用.

请记住dynamic(喜欢var)不是一种类型.它只是意味着"我不知道这是什么类型的编译时 - 我会让动态运行时查看实际类型,然后确定它如何处理它.

所以当你说:

dynamic d = new { World = "world" };
Run Code Online (Sandbox Code Playgroud)

d不是"一个dynamic".在运行时,值d将是匿名类型.

莫非动态粘结剂支持扩展方法?可能,但到目前为止*,增加的价值并不值得设计,实施,测试,运输和支持这样的功能的成本(相对于可以添加的其他东西).如果您认为这对框架是一个有价值的补充,那么请随时在http://connect.microsoft.com/上提交建议.

我还要注意,没有必要dynamic在你的(虽然做作的)场景中使用.如果您使用object而不是dynamic(并将扩展方法移动到静态类),您将获得完全相同的行为(使用扩展方法支持):

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();
}
Run Code Online (Sandbox Code Playgroud)

*至少在2011年,在这种情况下找到调用方法的问题被认为太难了 - 请参阅Eric Lippert的回答 - /sf/answers/371920461/:" ......这意味着要获得动态扩展方法调用正确解析,不知何故,DLR必须在运行时知道所有命名空间嵌套和使用指令都在源代码中.我们没有一个机制方便将所有信息编码到调用站点......"