理解LINQ中的执行

本文关键字:执行 LINQ 理解 | 更新日期: 2023-09-27 18:07:22

我有以下产品列表

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;
            }
        }

我知道我在这些话题上很晚了,但我仍然试图通过在Visual Studio中调试来理解

在所有函数(谓词,扩展方法)中都有断点

我的问题是

1。

执行下面一行时会发生什么?
var veges1 = products.Get(IsVege);   // i dont see the breakpoint hitting either predicate or GET method)

但在我的结果视图,而调试我看到输出为veges1。

如果我点击下面的代码

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

这是如何工作的?你能不能给点理解。

PS:我知道这方面有很多例子和问题。我试着用这个例子来理解,因为它会让我更容易得到东西。 <标题> 更新问题

我上面做的相同的样本我试图用lambda表达式做同样的事情

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

得到了预期的结果。

其中GET是我的扩展方法,但当那行被执行时,我的断点上GET方法从未被调用?

谢谢

理解LINQ中的执行

当使用yield return item;时,您返回一个枚举数,因此您应该这样做:

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

或使用.ToList()扩展方法,它将为您执行foreach循环并返回一个项列表。但是通过编写以下代码:

var item = products.Get(IsVege);

您刚刚收到了用于遍历所需集合的正确枚举数。

看到这是一个很好的参考如何调试yield return代码:什么是在c#中使用的yield关键字?

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;
}

LINQ和迭代器使用延迟执行。简而言之,yield等待,直到需要该值,然后继续执行。

veges1包含查询,而不是结果。只有当您对需要执行查询的查询执行操作时,工作才会完成。例如,为了给您veges1Count,需要完全执行Get。另一个例子是,如果执行veges1.First,则只需要执行Get循环的第一次迭代。

强制执行查询的一种常用方法是使用ToList:
// veges1 will now contain a List<Product> instead of an IEnumerable<Product>
var veges1 = products.Get(IsVege).ToList();

当你调用

var veges1 = products.Get(IsVege);

你只是传递给它一个匿名方法将在它的第一次迭代执行。IEnumerable——是一个延迟执行的接口,这意味着它的内容将在您进行第一次迭代时被填充。它只是描述你的行为,而不是实现它。您可以将代码更改为

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

这将引导您执行您的方法,因为ToList()将导致实现您的迭代。

IEnumerable在方法外构建更大的数据集时很好,有时您可以避免迭代它。