Reputation: 3813
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.
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
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
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
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