奇数行为随var,dynamic和linq组合而变化

我(懒惰地)在下面的代码的原始版本中使用了var ,并在代码的完全不同的部分中获得了一个奇怪的运行时exception。 将“var”更改为“int”修复了运行时exception,但我不明白为什么。 我把代码煮到了这个例子;

 public class Program { private static List Test(string i) { return new List {i}; } private static dynamic GetD() { return 1; } public static void Main() { int value1 = GetD(); // <-- int var result1 = Test("Value " + value1); // No problem, prints "Value 1", First() on List works ok. Console.WriteLine(result1.First()); var value2 = GetD(); // <-- var var result2 = Test("Value " + value2); // The below line gives RuntimeBinderException // 'System.Collections.Generic.List' does not contain a // definition for 'First' Console.WriteLine(result2.First()); } } 

我可以看到“var”的类型是动态的而不是int,但为什么该类型传播到并影响对Test()的调用的返回值的行为?

编辑:也许我应该澄清我的问题; 我可以看到dynamic传播到result2 ,我无法理解为什么,当IDE明确指出List Test(string)是被调用的方法时,它仍然将返回值推断为动态。 这是IDE比编译器更聪明的情况吗?

您的代码编译如下:

 public static void Main() { int value1 = GetD(); // <-- int List result1 = Test("Value " + value1); // No problem, prints "Value 1", First() on List works ok. Console.WriteLine(result1.First()); dynamic value2 = GetD(); // <-- var dynamic result2 = Test("Value " + value2); // The below line gives RuntimeBinderException // 'System.Collections.Generic.List' does not contain a // definition for 'First' Console.WriteLine(result2.First()); } 

result2是一个动态对象,然后不支持扩展方法(用作扩展方法)。

但是,您可以这样做:

 Console.WriteLine(Enumerable.First(result2)); 

UPDATE

你的IDE不是那么聪明。 尝试添加新方法:

 private static List Test(int i) { return new List { i }; } 

它将为您提供两种可能性。

UPDATE2

C#规范第7.6.5段:

如果至少满足下列条件之一,则动态绑定调用表达式(第7.2.2节):

  • primary-expression具有编译时类型dynamic。
  • 可选参数列表的至少一个参数具有编译时类型动态,而primary-expression没有委托类型。

问题是First是一个扩展方法而不是实例方法,而运行时绑定程序无法动态区分扩展方法和实例方法。

你可以在这里阅读更多内容:

扩展方法和动态对象

你可以看到下面的iamge显然可能是什么问题。

GetType()值

显示它的result2的GetType()是动态对象。

此外,dynamic关键字不提供对扩展方法的支持