此代码是否真的导致"访问修改后的闭包"问题?

PRI*_*UFF 6 .net c# linq closures

采用以下代码,Resharper告诉我voicesSoFarvoicesNeededMaximum导致"访问修改后的闭包".我读到了这些,但令我困惑的是Resharper建议通过在LINQ查询之前提取变量来解决这个问题.但这就是他们已经到了的地方!

如果我只是增加ReSharper的停止抱怨int voicesSoFar1 = voicesSoFar之后int voicesSoFar = 0.是否有一些我不理解的奇怪逻辑使得Resharper的建议正确无误?或者有没有办法安全地"访问修改后的闭包"在这样的情况下,而不会导致错误?

// this takes voters while we have less than 300 voices    
int voicesSoFar = 0;    
int voicesNeededMaximum = 300;    
var eligibleVoters =
    voters.TakeWhile((p => (voicesSoFar += p.Voices) < voicesNeededMaximum));
Run Code Online (Sandbox Code Playgroud)

jas*_*son 6

你将一个外部变量变为lambda表达式会产生一个非常讨厌的问题.问题是这样的:如果你尝试迭代eligibleVoters两次(foreach(var voter in eligibleVoters) { Console.WriteLine(voter.Name); }并且紧跟在(foreach(var voter in eligibleVoters) { Console.WriteLine(voter.Name); })之后你将看不到相同的输出.从功能编程的角度来看,这是不正确的.

这是一个扩展方法,它将累积,直到累加器上的某些条件为真:

public static IEnumerable<T> TakeWhileAccumulator<T, TAccumulate>(
    this IEnumerable<T> elements,
    TAccumulate seed,
    Func<TAccumulate, T, TAccumulate> accumulator,
    Func<TAccumulate, bool> predicate
) {
    TAccumulate accumulate = seed;
    foreach(T element in elements) {
        if(!predicate(accumulate)) {
            yield break;
        }
        accumulate = accumulator(accumulate, element);
        yield return element;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var eligibleVoters = voters.TakeWhileAccumulator(
                         0,
                         (votes, p) => votes + p.Voices, 
                         i => i < 300
                     );
Run Code Online (Sandbox Code Playgroud)

因此,上述说累积声音,而我们累积不到300票.

然后用:

foreach (var item in eligibleVoters) { Console.WriteLine(item.Name); }
Console.WriteLine();
foreach (var item in eligibleVoters) { Console.WriteLine(item.Name); }
Run Code Online (Sandbox Code Playgroud)

输出是:

Alice
Bob
Catherine

Alice
Bob
Catherine
Run Code Online (Sandbox Code Playgroud)