What are the benefits of a Deferred Execution in LINQ?

后端 未结 3 608
误落风尘
误落风尘 2020-11-22 14:04

LINQ uses a Deferred Execution model which means that resulting sequence is not returned at the time the Linq operators are called, but instead these operators return an obj

3条回答
  •  北荒
    北荒 (楼主)
    2020-11-22 14:56

    The main benefit is that this allows filtering operations, the core of LINQ, to be much more efficient. (This is effectively your item #1).

    For example, take a LINQ query like this:

     var results = collection.Select(item => item.Foo).Where(foo => foo < 3).ToList();
    

    With deferred execution, the above iterates your collection one time, and each time an item is requested during the iteration, performs the map operation, filters, then uses the results to build the list.

    If you were to make LINQ fully execute each time, each operation (Select / Where) would have to iterate through the entire sequence. This would make chained operations very inefficient.

    Personally, I'd say your item #2 above is more of a side effect rather than a benefit - while it's, at times, beneficial, it also causes some confusion at times, so I would just consider this "something to understand" and not tout it as a benefit of LINQ.


    In response to your edit:

    In your particular example, in both cases Select would iterate collection and return an IEnumerable I1 of type item.Foo. Where() would then enumerate I1 and return IEnumerable<> I2 of type item.Foo. I2 would then be converted to List.

    This is not true - deferred execution prevents this from occurring.

    In my example, the return type is IEnumerable, which means that it's a collection that can be enumerated, but, due to deferred execution, it isn't actually enumerated.

    When you call ToList(), the entire collection is enumerated. The result ends up looking conceptually something more like (though, of course, different):

    List results = new List();
    foreach(var item in collection)
    {
        // "Select" does a mapping
        var foo = item.Foo; 
    
        // "Where" filters
        if (!(foo < 3))
             continue;
    
        // "ToList" builds results
        results.Add(foo);
    }
    

    Deferred execution causes the sequence itself to only be enumerated (foreach) one time, when it's used (by ToList()). Without deferred execution, it would look more like (conceptually):

    // Select
    List foos = new List();
    foreach(var item in collection)
    {
        foos.Add(item.Foo);
    }
    
    // Where
    List foosFiltered = new List();
    foreach(var foo in foos)
    {
        if (foo < 3)
            foosFiltered.Add(foo);
    }    
    
    List results = new List();
    foreach(var item in foosFiltered)
    {
        results.Add(item);
    }
    

提交回复
热议问题