一旦结果不能再更改,C#LINQ聚合方法是否完成执行?

一旦结果不能再更改,C#LINQ聚合方法是否完成执行?,c#,linq,C#,Linq,我知道,如果我在同一个语句中将&&或| |运算符链接在一起,c#将停止计算该语句,并返回相应的结果(如果它对表达式求值),并且无论下面的表达式是什么,结果都不会改变。例如: var result = false && foo() && bar(); 在此语句中,foo()和bar()将永远不会执行,因为第一个表达式为false。我的问题是,Enumerable.Aggregate在运行一个bool列表时是否会执行相同的操作,或者它是否会计算所有表达式?例如: v

我知道,如果我在同一个语句中将
&&
| |
运算符链接在一起,c#将停止计算该语句,并返回相应的结果(如果它对表达式求值),并且无论下面的表达式是什么,结果都不会改变。例如:

var result = false && foo() && bar();
在此语句中,
foo()
bar()
将永远不会执行,因为第一个表达式为false。我的问题是,
Enumerable.Aggregate
在运行一个bool列表时是否会执行相同的操作,或者它是否会计算所有表达式?例如:

var result = new List<bool>
{
    false,
    foo(),
    bar()
}.Aggregate(true, (acc, x) => acc && x);
var结果=新列表
{
假,,
foo(),
bar()
}.聚合(真,(acc,x)=>acc和x);

您示例中的
&&
运算符将短路,是的
x
acc
false
的任何时候都不会被评估

当然,需要为每个元素执行整个谓词
Aggregate
无法知道以后执行它不会改变聚合值


聚合函数实际上是在数据集上执行
All
操作,如果使用
All
操作,而不是更一般的
Aggregate
,则只要给定谓词为
false
,它就能够确定结果是已知的且不能更改,并且不需要对其余元素调用谓词(当找到
true
值时,
也一样)。

正如其他答案所指出的,聚合调用将在整个集合上迭代,但每次调用上的&&将短路。以下是一个可能说明这一点的示例:

您可以看到,在result1中,一旦得到错误的结果,它就会停止计算x()。这是因为acc上存在短路

在result2中,我们将acc移到了&&的右边,它强制在所有情况下对x()求值

Console.WriteLine("acc && x()");
var result1 = new List<Func<bool>>
{
    () => {Console.WriteLine("One"); return true;},
    () => {Console.WriteLine("Two"); return false;},
    () => {Console.WriteLine("Three"); return false;}
}.Aggregate(true, (acc, x) => acc && x());
Console.WriteLine();
Console.WriteLine("x() && acc");
var result2 = new List<Func<bool>>
{
    () => {Console.WriteLine("One"); return true;},
    () => {Console.WriteLine("Two"); return false;},
    () => {Console.WriteLine("Three"); return false;}
}.Aggregate(true, (acc, x) => x() && acc);
现在,理论上,编译器可以将Aggregate()内联,获取lambda,并将其集成到内联聚合中,然后优化器可以识别出早期输出的潜力


怀疑当前C#编译器的优化器是否有那么大的攻击性,我不认为这是为它计划的,但它总是有可能的。

根据另一个问题,我使用的是
聚合
,如果出现某个中间值,我需要提前退出,这最终涉及到许多三元运算符(或带有
if
的lambda体),以在达到该值时绕过聚合,因此我编写了一些新的扩展来提前退出:

public static T AggregateWhile<T>(this IEnumerable<T> src, Func<T, T, T> accumFn, Predicate<T> whileFn) {
    using (var e = src.GetEnumerator()) {
        if (!e.MoveNext())
            throw new Exception("At least one element required by AggregateWhile");
        var ans = e.Current;
        while (whileFn(ans) && e.MoveNext())
            ans = accumFn(ans, e.Current);
        return ans;
    }
}

public static TAccum AggregateWhile<TAccum, T>(this IEnumerable<T> src, TAccum seed, Func<TAccum, T, TAccum> accumFn, Predicate<TAccum> whileFn) {
    using (var e = src.GetEnumerator()) {
        if (!e.MoveNext())
            throw new Exception("At least one element required by AggregateWhile");
        var ans = accumFn(seed, e.Current);
        while (whileFn(ans) && e.MoveNext())
            ans = accumFn(ans, e.Current);
        return ans;
    }
}
这将跳过执行
foo
bar


明显的对应项
Aggregate直到
s被留作练习:)

术语是“短路评估”
Aggregate()
将迭代整个可枚举项。不,累积函数对
Aggregate
不透明,因此它必须消耗整个序列。奇怪的投票模式。。。看来选民们并不真正了解这里发生了什么。
public static T AggregateWhile<T>(this IEnumerable<T> src, Func<T, T, T> accumFn, Predicate<T> whileFn) {
    using (var e = src.GetEnumerator()) {
        if (!e.MoveNext())
            throw new Exception("At least one element required by AggregateWhile");
        var ans = e.Current;
        while (whileFn(ans) && e.MoveNext())
            ans = accumFn(ans, e.Current);
        return ans;
    }
}

public static TAccum AggregateWhile<TAccum, T>(this IEnumerable<T> src, TAccum seed, Func<TAccum, T, TAccum> accumFn, Predicate<TAccum> whileFn) {
    using (var e = src.GetEnumerator()) {
        if (!e.MoveNext())
            throw new Exception("At least one element required by AggregateWhile");
        var ans = accumFn(seed, e.Current);
        while (whileFn(ans) && e.MoveNext())
            ans = accumFn(ans, e.Current);
        return ans;
    }
}
var result = new List<bool> {
    false,
    foo(),
    bar()
}.AggregateWhile(true, (acc, x) => acc && x, acc => acc);
var result = new List<Func<bool>> { () => false, () => foo(), () => bar() }
             .AggregateWhile(true, (acc, f) => acc && f(), acc => acc);