简体   繁体   English

Linq 中的 Take(x) 是否在获取 x 个对象时停止枚举?

[英]Does Take(x) in Linq stops enumerating when taking x objects?

For example, if I have this code:例如,如果我有以下代码:

    public static void Main(string[] args)
    {
        List<int> list = new List<int>() { 2, 3, 2, 9, 10, 2, 5 };

        var out = list.Where(x => x == 2).Take(2).ToList();
    }

Is the number of iterations 3 (as the second two is in index 2) or 7 (total number of elements)?迭代次数是 3(因为后两个在索引 2 中)还是 7(元素总数)?

Thanks谢谢

Yes, stops.是的,停止。

You can see this clearly by rewriting the code as follows:你可以通过如下重写代码清楚地看到这一点:

var result = list.Where(x =>
    {
        Console.WriteLine("Where: " + x);
        return x == 2;
    })
    .Take(2).ToList();

list will be iterated by the Where function, returning only matching items. list将由Where function 迭代,仅返回匹配项。
Where will be iterated by Take , which stops after 2 results. Where将被Take迭代,在 2 个结果后停止。
Take is fully iterated by ToList Take完全由ToList迭代

So the end result is that the iteration of list is stopped by Take at the second item of 2.所以最终的结果是list的迭代在2的第二项被Take停止了。

You can easily check it yourself.您可以自己轻松检查。 Let's test the hypothesis that 9 is reached (ie at least 4 items has been iterated):让我们测试一下达到9的假设(即至少迭代了4 个项目):

var result = list
  .Where(x => x == 2)        // your query under test
  .Take(2)
  .Select(item => item != 9  // business as usual for the first 3 items
     ? item                  // throw exception on the 4th
     : throw new Exception("Strange execution: 9 (4th item) has been scanned"))
  .ToList();                 // materialization executes the query

Run it and you'll see that 4th item ( 9 ) has not been taken: no exception has been thrown.运行它,你会看到第 4 项( 9 )没有被取走:没有抛出异常。

I think the most convincing (and simple) answer is to look at the source code of TakeIterator that runs when Take is called:我认为最有说服力(也是最简单)的答案是查看TakeIterator在调用Take时运行的源代码

static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count) 
{
    if (count > 0) {
        foreach (TSource element in source) {
            yield return element;
            if (--count == 0) break; // Yep, it stops after "count" iterations
        }
    }
}

If you write some test code with your own IEnumerable and IEnumerator, it will be easy to see what happens.如果你用自己的 IEnumerable 和 IEnumerator 编写一些测试代码,就很容易看出会发生什么。

class MyCollection : IEnumerable<int>
{
    public List<int> Data {get; set;} = new List<int>() { 2, 3, 2, 9, 10, 2, 5 };

    public IEnumerator<int> GetEnumerator()
    {
         return new MyEnumerator()
         {
             Data = this.Data,
         };
    }
}

And the enumerator:和枚举器:

class MyEnumerator : IEnumerator<int>
{
    private int index = -1;
    public List<int> Data {get; set;}

    public void Reset()
    {
        this.index = -1;
    }

    public bool MoveNext()
    {
        ++this.index;
        return this.index < this.Data.Count;
    }

    public int Current
    {
        get
        {
            int returnValue = this.Data[this.index];
            Debug.WriteLine("[{0}] {1}", this.index, returnValue);
            return returnValue;
        }
    }
}

Test code:测试代码:

void Main()
{
    MyCollection collection = new MyCollection();
    var out = collection.Where(x => x == 2).Take(2).ToList();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM