假设我们list.Where(p=>p.Number > n).Select(p=> p.Name).Where(n=> n.StartsWith(a)).ToList();将会运行一次通过算法,或者它将是3次通过?
list 只会在该代码中迭代一次,而不是3次.
当然,如果你想测试任何任意查询是否多次迭代源,它很容易通过实验测试,只需创建一个IEnumerable在你尝试多次迭代时抛出异常:
public static IEnumerable<T> ThereCanBeOnlyOne<T>(this IEnumerable<T> source)
{
return new SingleEnumerable<T>(source);
}
private class SingleEnumerable<T> : IEnumerable<T>
{
private bool hasRun = false;
private IEnumerable<T> wrapped;
public SingleEnumerable(IEnumerable<T> wrapped)
{
this.wrapped = wrapped;
}
public IEnumerator<T> GetEnumerator()
{
if (hasRun)
throw new InvalidOperationException(
"Sequence cannot be enumerated multilpe times");
else
{
hasRun = true;
return wrapped.GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
现在你可以写:
list.ThereCanBeOnlyOne()
.Where(p=>p.Number > n)
.Select(p=> p.Name)
.Where(n=> n.StartsWith(a))
.ToList();
Run Code Online (Sandbox Code Playgroud)
如果代码抛出异常,您尝试多次迭代基础列表.如果没有,你没有.
由于LINQ 流式传输数据的方式,它将在一次传递中构建列表.
例如,拿这个:
var query = list.Where(p => p.Number > n);
Run Code Online (Sandbox Code Playgroud)
这本身并没有查看列表中的任何元素.相反,它会记住您正在查看的列表,当您开始迭代时query,每次请求下一个元素时,它将依次检查列表中的元素,直到找到匹配为止 - 然后停止.例如:
using (var iterator = query.GetEnumerator())
{
iterator.MoveNext(); // This will look for the first match
Console.WriteLine(iterator.Current);
iterator.MoveNext(); // This will continue from just after the first match
}
Run Code Online (Sandbox Code Playgroud)
每个操作都以这种方式工作 - 所以当你得到时:
var query = list.Where(...)
.Select(...)
.Where(...);
Run Code Online (Sandbox Code Playgroud)
......当你要求第一个项目时query,它会链接回来(所以最后一个Where会询问结果Select,这将询问第一个的结果Where,它将询问列表)并继续前进直到得到结果.然后,当您要求下一个项目时,将询问下一个项目的结果Select,等等.
ToListList<T>立即从源头中的所有项目构建一个- 在这个意义上它是急切的(而不是其他懒惰的运算符).但原始列表本身仍将只迭代一次.
对于很多关于如何LINQ到对象更详细的工作-包括一个样本实现-你可能需要阅读我Edulinq博客系列.
| 归档时间: |
|
| 查看次数: |
166 次 |
| 最近记录: |