LINQ是否试图一次性解决问题?

Rel*_*lla 6 c# linq .net-3.5

假设我们list.Where(p=>p.Number > n).Select(p=> p.Name).Where(n=> n.StartsWith(a)).ToList();将会运行一次通过算法,或者它将是3次通过?

Ser*_*rvy 8

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)

如果代码抛出异常,您尝试多次迭代基础列表.如果没有,你没有.


Jon*_*eet 6

由于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博客系列.