2

我知道当我调用一些方法时,它开始执行链接到该集合实例的整个 LINQ 查询。

例如,每次我调用.Count(). 而且我认为每次我打电话给.ToList().

获得 ToList() 后,我对该集合进行“复制”,如果我不对集合的新实例编写任何 LINQ 查询,Count() 将不会执行任何操作。(正确的?)

如果我打电话给.Union()2 名单呢?并在 2 Ienumerable 上?和.Except()它是一样的吗?

尝试了一些跟踪/诊断,但我无法真正理解何时执行 LINQ 查询。

4

3 回答 3

2

Union 和 except 被延迟执行。阅读文档中的备注部分以获取更多信息。

http://msdn.microsoft.com/en-us/library/bb341731

http://msdn.microsoft.com/en-us/library/bb300779

此外,在这种情况下,当您确定幕后发生的事情时,您可以使用 Reflector 或任何其他 .NET 反编译器。这真的很有帮助。

于 2012-07-20T10:43:14.367 回答
1

对您的问题的回答取决于两个选项:

  • 操作类型:懒惰(或延迟)和贪婪。延迟操作不会立即执行,而是延迟到代码开始从您的 linq 源实现数据。贪婪操作总是立即执行。
    • 惰性操作示例:.Union, .Except, .Where, . Select和大多数其他 linq 操作
    • 贪心的是:.ToList.Count .ToArray以及所有实现数据的操作
  • linq 操作的数据源。当您使用 Linq to Memory 时,所有操作(懒惰和贪婪)都将立即执行。通常 Linq到外部数据源只会在物化期间执行惰性操作。

使用这两个规则,您可以预测 linq 的行为方式:

  1. .Count并将.ToList立即执行并实现数据
  2. 之后.ToList,您将在内存中收集,并且将立即执行所有后续操作(.Count将再次执行)
  3. .Union懒惰或贪婪的行为方式.Except取决于数据源的类型。对于内存,他们将是贪婪的,对于 SQL 是懒惰的。

LinqPad的示例。在使用贪婪或实现之前和之后,我有一个Enumerable懒惰或延迟.Where和操作:.Select.Count.ToList

void Main() 
{ 
    "get enumerable".Dump(); 
    var samplesEnumerable = GetSamples(); 

    "get count on enumerable #1".Dump(); 
    samplesEnumerable.Count().Dump(); 

    "get enumerable to list #1".Dump();  
    var list = samplesEnumerable.ToList();   

    "get count on list #1".Dump();   
    list.Count().Dump(); 

    "get count on list again #2".Dump(); 
    list.Count().Dump(); 

    "get where/select enumerable #1".Dump(); 
    samplesEnumerable 
        .Where(sample => { sample.Dump(); return sample.Contains("5"); }) 
        .Select(sample => { sample.Dump(); return sample; })
        .Dump(); 

    "get where/select list #1".Dump(); 
    list 
        .Where(sample => { sample.Dump(); return sample.Contains("5"); }) 
        .Select(sample => { sample.Dump(); return sample; })
        .Dump(); 
} 

string[] samples = new [] { "data1", "data2", "data3", "data4", "data5" }; 

IEnumerable<string> GetSamples() 
{ 
    foreach(var sample in samples)  
    { 
        sample.Dump(); 
        yield return sample; 
    } 
}

样本输出。关键点

  1. 在未具体化的数据上,每一次都在一次.Count又一次.List地检索数据

    • get count on enumerable #1
    • get where/select enumerable #1
  2. 物化数据后,将不再检索可枚举

    • get enumerable to list #1
    • get count on list #1
    • get count on list again #2
    • get where/select list #1

输出:

get enumerable
get count on enumerable #1
data1
data2
data3
data4
data5

5
get enumerable to list #1
data1
data2
data3
data4
data5
get count on list #1

5
get count on list again #2

5
get where/select enumerable #1
data1
data1
data2
data2
data3
data3
data4
data4
data5
data5
data5

data5 

get where/select list #1
data1
data2
data3
data4
data5
data5

data5
于 2012-07-20T11:01:40.203 回答
0

当 Linq 查询具有基础集合但它不是集合时,将执行该查询。例如,您可以使用ToList()或来实现查询ToArray

每个延迟执行Enumerable的扩展首先尝试将IEnumerable<T>转换为IList<T>(如果需要索引器,则为ICollection<T>fe)或(如果需要,则为 fe Count)。如果可以,它将使用不需要执行查询的“本机”方法,否则将枚举序列。

MSDN上搜索deferred项,查看方法是执行 xd deferred 还是立即执行。如果您检查源代码(fe via ),您可以通过查找.ILSpyyield keyword

UnionExcept使用延迟执行来实现。所以 如果你想保持这个结果,你需要一个ToList/ 。ToArray

这是一个示例,实现Enumerable.Count

ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
    return collection.Count;
}
ICollection collection2 = source as ICollection;
if (collection2 != null)
{
    return collection2.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        num++;
    }
}
return num;
于 2012-07-20T10:44:32.507 回答