使用 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&lt;string&gt; Test(string) 是调用的方法时,它仍然推断返回值是动态的。是不是 IDE 比编译器更聪明?

【问题讨论】:

您的 IDE 明确地为您提供了调用的方法?不是我的(Visual Stduio 2010 给出(动态表达))。你的 IDE 是什么? @CédricBignon Visual Studio 2012 与 ReSharper,单击调用并使用“转到定义”将跳转到方法。另外,如果我将调用更改为“Test2”,它会将调用标记为错误,告诉我没有这样的方法可以调用。 你的 IDE 没那么聪明。尝试添加新方法: private static List Test(int i) return new List i ; 它将为您提供两种可能性。 Extension method and dynamic object in c# 的可能重复项 【参考方案1】:

你的代码是这样编译的:

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&lt;string&gt; result2 = Test("Value " + value2); 而不是dynamic result2 = Test("Value " + value2); 不,因为 value2 是一个动态对象,编译器无法定义 Test 将使用哪个方法,因此无法确定它的返回值类型。 我们只有一个Test 方法,返回类型为List&lt;string&gt; 是的,没错,但编译器仍然“更喜欢”将其视为动态表达式。 好的,我明白了,但是为什么呢?规范的任何链接。【参考方案2】:

问题在于 First 是扩展方法而不是实例方法,并且运行时绑定器在动态区分扩展方法和实例方法时遇到了麻烦。

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

Extension method and dynamic object

【讨论】:

【参考方案3】:

您可以看到下面的 iamge 清楚地显示了可能是什么问题。

result2 的 GetType() 表明它是动态对象。

另外,动态关键字不支持扩展方法

【讨论】:

以上是关于使用 var、动态和 linq 组合的奇怪行为变化的主要内容,如果未能解决你的问题,请参考以下文章

Linq 的奇怪行为

路线变化的奇怪行为

UITableView 滚动时奇怪的布局行为变化

ListView 中绑定属性和直接可见性变化的奇怪行为

关于ManyToMany过滤和组合查询集的非常奇怪的行为

奇怪的变化检测行为Angular 2+