CLR如何解释以下LINQ查询

时间:2014-01-08 07:49:15

标签: c# linq

我有2个obj1obj2

列表
var list1 = new List<obj1>();
var list2 = new List<obj2>();

obj1和obj2共享一个名为name的字符串属性,我需要通过list2中可用的name属性值过滤list1,所以我做了以下

var filteredlist = list1.Where(o => list2.Select(o2 => o2.name)
                                         .Distinct()
                                         .Contains(o.name));

以上Linq查询是否等同于以下内容?

 var distinctNames = list2.Select(o2 => o2.name).Distinct();
 var filteredlist = list1.Where(o => distinctNames.Contains(o.name));

我的问题是在第一个查询中clr是否创建了一个临时变量来保存distinctNames,即使我没有像第二个查询那样自己创建它?或者它会在每次迭代时从list2重做Select Distinct吗?

如果它没有创建临时变量,你会如何在一行中编写这个查询?

3 个答案:

答案 0 :(得分:8)

LINQ只是IEnumerable接口上定义的一组扩展方法,因此它不是“CLR如何做到这一点”的问题。您的LINQ查询将等同于

Func<bool> innerAction = list2.Select(o2 => o2.name).Distinct().Contains(o => o.name);
foreach(var e1 in list1)
{
    bool condition = innerAction();
    if (condition)
    {
        yield return e1;
    }
}

选择查询将类似于

Func<TIn, TOut> selectFunction = e => e.name;
foreach(var e2 in list2)
{
    yield return selectFunction(e2);
}

结果将传递给distinct函数,这是另一个foreach循环,并将传递给contains函数,这将导致另一个foreach循环。

所以你的问题的答案是“是的,它们或多或少相当”,但它涉及很多lambdas :)并且“是的,它会在每次迭代时重做Distinct”,因为它再次调用生成的lambda。您最好通过innerActionToArray()从我的第一个列表中强制评估ToList()(在您的情况下,在where条件的内部操作上调用它)。

答案 1 :(得分:2)

Matten的回答是完全正确的,但是,我想指出,有一种简单的方法可以确保Distinct不会一次又一次地完成。您所要做的就是先将IEnumerable强制转换为实际数据,即:

var distinctNames = list2.Select(o2 => o2.name).Distinct().ToList();
var filteredlist = list1.Where(o => distinctNames.Contains(o.name));

当然,在列表中搜索是线性操作,因此在最坏的情况下,您正在进行distinctNames.Length * list1.Length比较。如果您改用词典:

var distinctNames = list2.GroupBy(i => i.name).ToDictionary(i => i.Key, i => true);
var filteredList = list1.Where(i => distinctNames.ContainsKey(i.name));

Voilá,现在查找是在更接近n * log(n)时间的情况下完成的:)但是,请注意,这只有在list1不小于list2时才有意义 - 在建立字典可能会很好地抵消以后的性能提升。始终使用正确的工具:P

答案 2 :(得分:1)

LINQ(对象)本质上是一个扩展方法的框架,用于构建函数表达式的组合,这些函数表达式应用于一系列值,这些值由.Net库中的IEnumerable<T1>表示。 CLR不需要知道这个:对于CLR,LINQ只是lambdas和扩展方法。

您的问题的有趣之处在于LINQ查询期间的执行流程。在两种情况下,根本不执行任何操作。在迭代生成的IEnumerable<T2>之前,不会评估LINQ查询。这也意味着,在两种情况下都会评估不同名称的查询。

我认为在扩展方法语法中无法阻止此评估。但是可以这样:

var distinctNames = list2.Select(o2 => o2.name).Distinct().ToArray();
var filteredlist = list1.Where(o => distinctNames.Contains(o.name));

或者您可以尝试将LINQ查询语法与let语句一起使用。但我认为这两个陈述使意图更清晰。