使用 var、动态和 linq 组合的奇怪行为变化
Posted
技术标签:
【中文标题】使用 var、动态和 linq 组合的奇怪行为变化【英文标题】:Odd behaviour change with var, dynamic and linq combination 【发布时间】:2013-01-10 05:56:31 【问题描述】:我(懒惰地)在以下代码的原始版本中使用了var
,并在代码的完全不同部分得到了一个奇怪的运行时异常。将“var”更改为“int”修复了运行时异常,但我不太明白为什么。我把代码归结为这个例子;
public class Program
private static List<string> Test(string i) return new List<string> 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<string> works ok.
Console.WriteLine(result1.First());
var value2 = GetD(); // <-- var
var result2 = Test("Value " + value2);
// The below line gives RuntimeBinderException
// 'System.Collections.Generic.List<string>' does not contain a
// definition for 'First'
Console.WriteLine(result2.First());
我可以看到“var”的类型是动态的而不是 int,但为什么该类型会传播到并影响对Test()
的调用的返回值的行为?
编辑:也许我应该澄清我的问题;我可以看到dynamic
传播到result2
,我无法理解的是为什么当IDE 清楚地表明List<string> Test(string)
是调用的方法时,它仍然推断返回值是动态的。是不是 IDE 比编译器更聪明?
【问题讨论】:
您的 IDE 明确地为您提供了调用的方法?不是我的(Visual Stduio 2010 给出(动态表达))。你的 IDE 是什么? @CédricBignon Visual Studio 2012 与 ReSharper,单击调用并使用“转到定义”将跳转到方法。另外,如果我将调用更改为“Test2”,它会将调用标记为错误,告诉我没有这样的方法可以调用。 你的 IDE 没那么聪明。尝试添加新方法: private static List你的代码是这样编译的:
public static void Main()
int value1 = GetD(); // <-- int
List<string> result1 = Test("Value " + value1);
// No problem, prints "Value 1", First() on List<string> works ok.
Console.WriteLine(result1.First());
dynamic value2 = GetD(); // <-- var
dynamic result2 = Test("Value " + value2);
// The below line gives RuntimeBinderException
// 'System.Collections.Generic.List<string>' does not contain a
// definition for 'First'
Console.WriteLine(result2.First());
result2
是动态对象,不支持扩展方法(作为扩展方法使用)。
但是,您可以这样做:
Console.WriteLine(Enumerable.First(result2));
更新
您的 IDE 并不那么聪明。尝试添加新方法:
private static List<int> Test(int i) return new List<int> i ;
它将为您提供两种可能性。
更新2
C# 规范第 7.6.5 段:
如果至少有一个调用表达式是动态绑定的(第 7.2.2 节) 以下持有:
主表达式具有编译时类型动态。 可选参数列表中的至少一个参数具有编译时类型动态,而主表达式没有委托类型。
【讨论】:
var result2 = Test("Value " + value2);
将被编译为List<string> result2 = Test("Value " + value2);
而不是dynamic result2 = Test("Value " + value2);
。
不,因为 value2 是一个动态对象,编译器无法定义 Test 将使用哪个方法,因此无法确定它的返回值类型。
我们只有一个Test
方法,返回类型为List<string>
。
是的,没错,但编译器仍然“更喜欢”将其视为动态表达式。
好的,我明白了,但是为什么呢?规范的任何链接。【参考方案2】:
问题在于 First 是扩展方法而不是实例方法,并且运行时绑定器在动态区分扩展方法和实例方法时遇到了麻烦。
你可以在这里阅读更多内容:
Extension method and dynamic object
【讨论】:
【参考方案3】:您可以看到下面的 iamge 清楚地显示了可能是什么问题。
result2 的 GetType() 表明它是动态对象。
另外,动态关键字不支持扩展方法
【讨论】:
以上是关于使用 var、动态和 linq 组合的奇怪行为变化的主要内容,如果未能解决你的问题,请参考以下文章