3

我有以下产品列表

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:我知道这有很多例子和问题。我试图用这个例子来理解,因为它会让我更容易得到东西。

更新的问题

我在上面做的同一个样本试图对 Lamda Expression 做同样的事情

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

我得到了预期的结果。

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

谢谢

4

3 回答 3

2

使用yield return item;时返回一个 Enumerator 所以你应该做这样的事情:

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

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

var item = products.Get(IsVege);

您刚刚收到了用于遍历所需集合的适当枚举器。

有关如何调试yield return代码的良好参考,请参阅此: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;
}
于 2013-04-03T07:44:17.290 回答
1

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

veges1包含查询,而不是结果。仅当您对需要执行查询的查询执行操作时才完成工作。例如,要给你Countof veges1Get需要完全执行。再举一个例子,如果你执行veges1.First了,那么只有你的循环的第一次迭代Get需要被执行。

强制执行查询的常用方法是使用ToList

// veges1 will now contain a List<Product> instead of an IEnumerable<Product>
var veges1 = products.Get(IsVege).ToList();
于 2013-04-03T07:42:41.513 回答
1

你打电话时

var veges1 = products.Get(IsVege);

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

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

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

在方法之外构建更大的数据集时,IEnumerable 非常有用,有时您可以避免对其进行迭代。

于 2013-04-03T07:47:58.373 回答