2

Can anyone please tell me the difference between the following two lambda expressions:

    1. TabView mytab = TabCollection.Where(s => s.TabHeader == h).FirstOrDefault();

    2. TabView mytab = TabCollection.FirstOrDefault(s => s.TabHeader == h);

TabCollection is an ObservableCollection of type TabView.

rock
  • 585
  • 3
  • 10
  • 26
  • 2
    I don't see any difference here. – MarcinJuraszek Jul 15 '13 at 21:54
  • 1
    [Order of LINQ extension methods does not affect performance?](http://stackoverflow.com/questions/10110013/order-of-linq-extension-methods-does-not-affect-performance) – Tim Schmelter Jul 15 '13 at 21:57
  • `Less code + same results` = `Better Code`. Use the second approach. type less. delay carpal tunnel. – Federico Berasategui Jul 15 '13 at 21:57
  • When you separate both methods from each other you have more options. You can use the `Where` query lazily, so on each iteration or method call it could yield a different result. You could also chain other linq methods if desired(e.g. `OrderBy` or `Take(n)`) whereas the `FirstOrDefault` "ends" the query. However, the second approach is a little bit more effficient and - more important - more readable since it's more direct. – Tim Schmelter Jul 15 '13 at 22:02

2 Answers2

5

The two statements will provide the same results.

The difference is in how the result is achieved. The second is slightly more efficient, as it does not need to generate an iterator for the Where method, and then get it's enumerator, and can instead directly enumerate the collection until a match is found.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 3
    the difference is incredibly slight – undefined Jul 15 '13 at 21:57
  • 1
    @LukeMcGregor There is an extra iterator allocated for handling the Where enumeration - it's minor, but in a tight loop or performant code, avoiding allocations can be important. It'd be tough to measure in anything except a tight loop, though, since any gain would likely be reduced GC pressure more than saved CPU cycles. – Reed Copsey Jul 15 '13 at 21:58
  • @ReedCopsey - Good point about GC pressure :) Space complexity is often overlooked. – Travis J Jul 15 '13 at 21:59
  • Essentially, the two options are nearly identical - but they aren't technically identical, and the second is potentially better (hence the overload exists) – Reed Copsey Jul 15 '13 at 21:59
  • @Luke - The entirety of the `Where` condition must be met before continuing on to the next method. This means that if there are millions of items in the collection the `Where` method would need to visit millions of items to see if the condition were met. Then the next method `FirstOrDefault` would take the first match of the iterator. Whereas the `FirstOrDefault` method will break out at the first match, potentially saving millions of conditional checks. – Travis J Jul 15 '13 at 22:05
  • 1
    @TravisJ That is completely untrue. The Where() method returns an *iterator*, not a fully populated collection. When `FirstOrDefault` starts enumerating the `IEnumerable` returned from `Where`, it enumerates it in a deferred, streaming manner. Both versions will still short circuit and stop checking once a match is found. – Reed Copsey Jul 15 '13 at 22:14
  • @ReedCopsey - So the first yield returned value from where will make it to firstordefault and cause the execution to stop at that point? My point was that building the enumerable in the `Where` method requires checking every item. Is that not the case? Does the `FirstOrDefault` method check for values when `Where` starts building the enumerable? – Travis J Jul 15 '13 at 22:37
  • 2
    @TravisJ Yes - the first yield returned which exists (from first or default) will "stop" the Where iterator from continuing. `Where` does not need to check every item - it will only check items as FirstOrDefault enumerates them, which means it'll only check until a match is found and then stop. – Reed Copsey Jul 15 '13 at 22:41
3
TabCollection.Where(s => s.TabHeader == h).FirstOrDefault()

This creates WhereIterator and returns it. Then you starting iteration and return first element of it. That looks like

var iterator = new WhereEnumerableIterator<TSource>(TabCollection, predicate);

using (IEnumerator<TSource> enumerator = iterator.GetEnumerator())
{
   if (enumerator.MoveNext())
       return enumerator.Current;
}

return default(TSource);

Second one does not create iterator - it simply enumerates over source:

TabCollection.FirstOrDefault(s => s.TabHeader == h);

Same as

foreach (TSource local in TabCollection)
{
    if (predicate(local))        
        return local;        
}

return default(TSource);

So, second option is slightly more efficient.

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459