8

I have an IEnumerable<T> that I wanted to filter based on a LINQ predicate. I tried using Where on the IEnumerable as I normally do, but this time I stumbled upon something interesting. When calling Where on the IEnumerable, with the predicate, i get an empty list in return. I know it has to produce a list with two items in it. If I instead use FindAll, with the same predicate, it then produces the correct result.

Can anyone explain to me, why this is happening? I always thought that Where was kind of a lazy version of FindAll, that also returned an IEnumerable instead of a List. There must be more to it than that? (I have done some research, but to no avail.)

Code:

IEnumerable<View> views = currentProject.Views.Where(
                    v => v.Entries.Any(e => e.Type == InputType.IMAGE || e.Type == InputType.VIDEO));

IEnumerable<View> views = currentProject.Views.FindAll(
                    v => v.Entries.Any(e => e.Type == InputType.IMAGE || e.Type == InputType.VIDEO));
ThomasCle
  • 6,792
  • 7
  • 41
  • 81
Teilmann
  • 2,146
  • 7
  • 28
  • 57
  • 12
    `List.FindAll` returns a `List` whereas `Where` returns an `IEnumerable`. But `Where` is using deferred execution, so you only get it when you _materialize_ it, f.e. with `ToList`. – Tim Schmelter Oct 12 '15 at 09:06
  • 6
    Why didn't you post that as an answer ? :) Better explanation than the only answer here, which was posted later than your comment. – Teilmann Oct 12 '15 at 09:11
  • Even though your comment answered the question, an answer has been posted, which answers it more thoroughly than any of the answers on the linked question. – Teilmann Oct 12 '15 at 09:33
  • That i am aware of :) – Teilmann Oct 12 '15 at 10:30

3 Answers3

4

You can find your answer here: LINQ, Where() vs FindAll() . Basically if you call .ToList() on your "Where" they would be the same.

You can find more about the differences between deferred and immediate execution: https://code.msdn.microsoft.com/LINQ-Query-Execution-ce0d3b95

Community
  • 1
  • 1
dlght
  • 1,406
  • 1
  • 18
  • 35
  • I know that what you state is probably a simplified explanation of the differences between the two, but i get the idea. Thanks! – Teilmann Oct 12 '15 at 09:10
2

My best guess would be that something happens between calling Where, which creates an enumerator and the place in your code where the results are actually used (i.e. where MoveNext and (get_)Current of that enumerator are actually called, e.g. from ToList).

Primary Key
  • 1,237
  • 9
  • 14
  • The problem here was, that i didn't call .ToList() when using Where() on the IEnumerable, so inspecting it showed an empty list. – Teilmann Oct 12 '15 at 09:19
2

Yes, Where is a lazy version of findall. FindAll() is a function on the List type, it's not a LINQ extension method like Where. The FindAll method on List, which is an instance method that returns a new List with the same element type. FindAll can only be used on List instances whereas LINQ extension methods work on any type that implements IEnumerable.

The main difference (besides what they're implemented on: IEnumerable vs. List) is that Where implements deferred execution, where it doesn't actually do the lookup until you need it, (using it in a foreach loop for example). FindAll is an immediate execution method.

I will refer to a data structure called an expression tree to understand deferred execution, you need only grasp that an expression tree is a data structure like a list or queue. It holds a LINQ to SQL query not the results of the query, but the actual elements of the query itself.

To understand the Where Working we need to see that if we write a code

var query = from customer in db.Customers  
        where customer.City == "Paris"
        select customer;              

Query does not execute here whereas it execute in the foreach loop

TO understand LINQ and Deferred Execution

Shweta Pathak
  • 775
  • 1
  • 5
  • 21