IList vs IEnumerable:当他们“执行”LINQ查询时?

时间:2012-07-20 10:34:46

标签: linq

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

示例,每次拨打.Count()时。我想每次拨打.ToList()时也会这样。

获取ToList()之后,我对该集合执行“复制”,而不是在集合的新实例上没有写任何LINQ查询,Count()将不会执行任何操作。 (右?)

如果我在2个名单上拨打.Union()怎么办?并且2个不可数?和.Except()它是一样的吗?

尝试使用一些Trace / Diagnostic,但我无法理解何时执行LINQ查询。

3 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:1)

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

  • 操作类型:懒惰(或延迟)和贪婪。懒惰操作不会立即执行,并且在代码开始实现来自linq源的数据之前一直推迟。贪婪的行动总是立即执行。
    • 延迟操作的示例:.Union.Except.Where,。Select以及大多数其他linq操作
    • 贪婪:.ToList.Count .ToArray以及实现数据的所有操作
  • 您的linq操作的
  • 数据来源。当您使用Linq to Memory时,所有操作(懒惰和贪婪)都将立即执行。 Usualy Linq 到外部数据源只会在实现过程中执行延迟操作。

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

  1. .Count.ToList将立即执行并实现数据
  2. .ToList之后,您将获得内存收集,并且所有后续操作将立即执行(.Count将再次执行一次)
  3. .Union.Except如何表现为懒惰或贪婪取决于数据源的类型。对于内存,他们会贪婪,因为SQL懒惰。
  4. 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
    3. 输出:

      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
      

答案 2 :(得分:0)

Linq查询在具有基础集合但 不是集合时执行。您可以使用ToList()ToArray来实现查询。

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

MSDN上搜索tem deferred ,以查看方法是延迟执行还是立即执行。如果您检查源代码(例如,通过ILSpy),您可以通过查找yield 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;