IList vs IEnumerable:当它们;执行;LINQ查询?

IList vs IEnumerable:当它们;执行;LINQ查询?,linq,Linq,我知道,当我调用一些方法时,它开始执行链接到集合实例的整个LINQ查询 例如,每次我调用.Count()。我想每次我调用.ToList()时 在获得ToList()之后,我对该集合进行“复制”,如果我不对该集合的新实例编写任何LINQ查询,Count()将不会执行任何操作。(对吗?) 如果我调用列表2中的.Union(),会怎么样?而在2-Ienumerable上呢?与.Except()相同吗 尝试了一些跟踪/诊断,但我无法真正理解何时执行LINQ查询。延迟执行Union和Except。有关更多

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

例如,每次我调用
.Count()
。我想每次我调用
.ToList()

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

如果我调用列表2中的
.Union()
,会怎么样?而在2-Ienumerable上呢?与
.Except()
相同吗


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

延迟执行Union和Except。有关更多信息,请阅读文档中的备注部分


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

当Linq查询有一个底层集合,但不是集合时,就会执行该查询。例如,您可以使用
ToList()
ToArray
对查询进行材质化

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

搜索延迟打开的tem,查看方法是延迟执行还是立即执行。如果您检查源代码(例如,通过
ILSpy
),您可以通过查找来检测延迟执行的方法

Union
Except
使用延迟执行来实现。因此,如果要持久化该结果,您需要一个
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;
ICollection collection=源作为ICollection;
if(集合!=null)
{
返回集合。计数;
}
ICollection collection2=源作为ICollection;
if(collection2!=null)
{
返回集合2.计数;
}
int num=0;
使用(IEnumerator enumerator=source.GetEnumerator())
{
while(枚举数.MoveNext())
{
num++;
}
}
返回num;

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

  • 操作类型:懒惰(或延迟)和贪婪。延迟操作不会立即执行,直到代码开始从linq源具体化数据时才会执行。贪婪操作总是立即执行。
    • 惰性操作的示例:
      .Union
      。除了
      。其中
      ,.
      选择
      和大多数其他linq操作
    • 贪婪的有:
      .ToList
      .Count
      .ToArray
      和所有具体化数据的操作
  • 用于linq操作的数据源。当您使用Linq存储时,所有操作(包括懒惰和贪婪)都将立即执行。通常,Linq到外部数据源只会在具体化期间执行延迟操作
使用这两条规则,您可以预测linq的行为:

  • .Count
    .ToList
    将立即执行并具体化数据
  • .ToList
    之后,您将在内存中获取集合,并且将立即执行以下所有操作(
    .Count
    将再次执行一个操作)
  • .Union
    .Except
    如何表现为懒惰或贪婪取决于数据源的类型。对于内存,他们将是贪婪的,对于SQL则是懒惰的
  • 例如。我有一个
    可枚举的
    和延迟的
    。其中
    。使用贪婪
    在具体化前后对其选择
    操作。计数
    。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; 
        } 
    }
    

    在这种情况下,使用reflector或其他反编译器工具来确保“Linq查询是在查询而不是集合时执行的”这句话毫无意义。所以每次我使用贪婪操作时,它都会执行附加到集合的所有查询?这就是我想知道的…我的意思是:我有一个IEnumerable,我对它做了4个查询。如果我执行Count(),我将执行这4个查询。然后,如果我再次执行Count(),我将重新执行这4个查询。但是如果我把IEnumerable变成一个列表,如果我计算()2次,会发生什么?再次执行查询2次?是的,linq构建了惰性命令的表达式树(在适当的数据源上),并在到达贪婪命令时执行整个树。查看其工作原理的简单实用方法是附加到SQL server的日志,并在Debugger或linqpad中逐个添加惰性命令。或者看第9频道:(LINQ Beyond Queries)[和(LINQ技巧和优化)[Scott Allen;)首先,你能指定LINQ-2-what吗?你说的是内存还是非内存?是的,我使用LINQ从SQL管理我的对象(在内存中)
    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