【问题标题】:IList vs IEnumerable : when they "executes" the LINQ query?IList vs IEnumerable:当他们“执行”LINQ查询时?
【发布时间】:2012-07-20 10:34:45
【问题描述】:

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

例如,每次我打电话给.Count()。而且我想每次我打电话给.ToList()

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

如果我在 2 列表中调用 .Union() 会怎样?并在 2 Ienumerable 上?和.Except()一样吗?

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

【问题讨论】:

  • 在这种情况下使用反射器或其他反编译器工具来确定引擎盖下发生了什么

标签: linq


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    1. .Count.ToList 将立即执行并具体化数据
    2. .ToList之后,你会在内存中获取collection,所有后续操作都会立即执行(.Count会再执行一次)
    3. .Union.Except 将如何表现为惰性或贪婪取决于数据源的类型。对于内存,它们会贪婪,对于 SQL 则很懒。

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

    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
    

    【讨论】:

    • 所以每次我使用贪婪操作时,它都会执行所有附加到集合的查询?这就是我想知道的......
    • 我的意思是:我有一个 IEnumerable 并且我对它进行了 4 次查询。如果我执行 Count(),我将执行这 4 个查询。比,如果我再做一次 Count() 我将重新执行那 4 个查询。但是如果我将 IEnumerable 变成 List,并且如果我执行 Count() 2 次,会发生什么?查询又执行了 2 次?
    • 是的,linq 构建惰性命令的表达式树(在适当的数据源上),并在到达贪婪命令时执行整个树。查看它如何工作的简单实用方法是附加到 SQL Server 的日志并在调试器或 linqpad 中一一添加惰性命令。或观看第 9 频道:(LINQ Beyond Queries)[channel9.msdn.com/Blogs/matthijs/… 和(LINQ Tips Tricks and Optimizations)[channel9.msdn.com/Blogs/matthijs/… by Scott Allen ;)
    • 首先,你能指定linq-2-什么吗?你说的是不是L内存?
    • 是的,我使用 LINQ 从 SQL 管理我的对象(在内存中)
    【解决方案3】:

    当一个 Linq 查询一个基础集合但它不是一个集合时,它会被执行。例如,您可以使用ToList()ToArray 实现查询。

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

    MSDN 上搜索项目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;
    

    【讨论】:

    • “当 Linq 查询是查询而不是集合时执行”没有意义。
    猜你喜欢
    • 2011-04-05
    • 1970-01-01
    • 1970-01-01
    • 2015-04-01
    • 1970-01-01
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多