user2067567
user2067567

Reputation: 3813

Understanding Execution in LINQ

I have Following list of products

List<Product> products = new List<Product> {
                new Product { 
                    ProductID = 1, 
                    ProductName = "first candy", 
                    UnitPrice = (decimal)10.0 },
                new Product { 
                    ProductID = 2, 
                    ProductName = "second candy", 
                    UnitPrice = (decimal)35.0 },
                new Product { 
                    ProductID = 3, 
                    ProductName = "first vegetable", 
                    UnitPrice = (decimal)6.0 },
                new Product { 
                    ProductID = 4, 
                    ProductName = "second vegetable", 
                    UnitPrice = (decimal)15.0 },
                new Product { 
                    ProductID = 5, 
                    ProductName = "third product", 
                    UnitPrice = (decimal)55.0 }
            };

var veges1 = products.Get(IsVege);   //Get is a Extension method and IsVege is a Predicate

//Predicate
 public static bool IsVege(Product p)
        {
            return p.ProductName.Contains("vegetable");
        }

//Extension Method
public static IEnumerable<T> Get<T>(this IEnumerable<T> source, Func<T, bool> predicate)
        {
            foreach (T item in source)
            {
                if (predicate(item))
                    yield return item;
            }
        }

I know am very late on these topics but still am trying to understand by debugging in Visual Studio

i have break point in all the functions(Predicate,Extension Method)

My question is

1.What happens when the below line gets executed

var veges1 = products.Get(IsVege);   // i dont see the breakpoint hitting either predicate or GET method)

But in my results view while debugging i see output for veges1.

If i hit the below code

veges1.Count()   // Breakpoint in Predicate and GET is hit and i got the count value.

How does this work ? Can you give some understanding.

PS: i know there are lot of examples and questions on this. Am trying to understand with this example as it ll make me easier to get things.

Updated Question

The same sample i did above am trying to do the same with Lamda Expression

var veges4 = products.Get(p => p.ProductName.Contains("vegetable"));

and i get results as expected.

where GET is my Extension method but when that line is executed my break points on GET method was never called?

Thanks

Upvotes: 3

Views: 164

Answers (3)

Alex
Alex

Reputation: 8937

When you call

var veges1 = products.Get(IsVege);

you just assing to it an anonymous method will be executed on its first iteration. IEnumerable - is an interface with deferred execution, and that means that its content will be filled when you make your first iteration. It just describes your behavior, but not implements it. You can change your code to

var veges1 = products.Get(IsVege).ToList();

This will lead you to the executon of your method, because ToList() will cause the implementation of your iteration.

IEnumerable are good when building a larger set of data outside the method, and sometimes you can avoid of iteratin it.

Upvotes: 1

Mortalus
Mortalus

Reputation: 10712

When using yield return item; you return an Enumerator so you should do something like this:

foreach (var item in products.Get(IsVege))
{...}

or use .ToList() extension method that will do the foreach loop for you and return a list of items. but by writing the following code:

var item = products.Get(IsVege);

you just received the proper enumerator for traversing the desired collection.

see this for a good referance on how to debug the yield return code: What is the yield keyword used for in C#?

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

Upvotes: 2

Heinzi
Heinzi

Reputation: 172478

LINQ and iterators use deferred execution. In a nutshell, yield waits until the value is needed and then continues execution.

veges1 contains a query, not a result. Work is done only when you perform an operation on the query that requires the query to be executed. For example, to give you the Count of veges1, Get needs to be executed fully. As another example, if you executed veges1.First, only the first iteration of your Get loop would need to be executed.

A common way to force execution of your query is to use ToList:

// veges1 will now contain a List<Product> instead of an IEnumerable<Product>
var veges1 = products.Get(IsVege).ToList();

Upvotes: 1

Related Questions