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

请考虑以下代码:

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映射到dynamic ? 这对我来说没有意义。

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


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

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

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

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

 var result = test.ExtensionMethod(d); 

由编译器转换为

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

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

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

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

所以当你说:

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

d不是“ dynamic ”。 在运行时, d的值将是匿名类型。

动态绑定器可以支持扩展方法吗? 可能,但到目前为止* ,增加的价值并不值得设计,实施,测试,运送和支持这样的function的成本(相对于可以添加的其他东西)。 如果您认为这对框架是一个有价值的补充,那么请随时在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(); } 

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