[英]Does Take(x) in Linq stops enumerating when taking x objects?
例如,如果我有以下代码:
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();
}
迭代次数是 3(因为后两个在索引 2 中)还是 7(元素总数)?
谢谢
是的,停止。
你可以通过如下重写代码清楚地看到这一点:
var result = list.Where(x =>
{
Console.WriteLine("Where: " + x);
return x == 2;
})
.Take(2).ToList();
list
将由Where
function 迭代,仅返回匹配项。
Where
将被Take
迭代,在 2 个结果后停止。
Take
完全由ToList
迭代
所以最终的结果是list
的迭代在2的第二项被Take
停止了。
您可以自己轻松检查。 让我们测试一下达到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
运行它,你会看到第 4 项( 9
)没有被取走:没有抛出异常。
我认为最有说服力(也是最简单)的答案是查看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
}
}
}
如果你用自己的 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,
};
}
}
和枚举器:
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;
}
}
}
测试代码:
void Main()
{
MyCollection collection = new MyCollection();
var out = collection.Where(x => x == 2).Take(2).ToList();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.