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

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

如果我只是在int voicesSoFar = 0之后添加int voicesSoFar1 = voicesSoFar Resharper就会停止抱怨。 是否有一些我不理解的奇怪逻辑使得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)); 

你有一个非常讨厌的问题,因为将外部变量变为lambda表达式。 问题是这样的:如果你试图两次迭代eligibleVoters foreach(var voter in eligibleVoters) { Console.WriteLine(voter.Name); }foreach(var voter in eligibleVoters) { Console.WriteLine(voter.Name); }并紧接着( foreach(var voter in eligibleVoters) { Console.WriteLine(voter.Name); } )你不会看到相同的输出。从function编程的角度来看,这是不正确的。

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

 public static IEnumerable TakeWhileAccumulator( this IEnumerable elements, TAccumulate seed, Func accumulator, Func predicate ) { TAccumulate accumulate = seed; foreach(T element in elements) { if(!predicate(accumulate)) { yield break; } accumulate = accumulator(accumulate, element); yield return element; } } 

用法:

 var eligibleVoters = voters.TakeWhileAccumulator( 0, (votes, p) => votes + p.Voices, i => i < 300 ); 

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

然后:

 foreach (var item in eligibleVoters) { Console.WriteLine(item.Name); } Console.WriteLine(); foreach (var item in eligibleVoters) { Console.WriteLine(item.Name); } 

输出是:

 Alice Bob Catherine Alice Bob Catherine 

好吧,错误信息是正确的,因为在操作期间不保留voicesSoFar的值。 在纯粹的“function性”术语中(而lambda实际上是为了在function上起作用),它会让人感到困惑。

例如,一个有趣的测试将是:

如果我迭代查询两次会发生什么?

例如:

 int count = voters.Count(); var first = voters.FirstOrDefault(); 

我相信你可以看到… 10null – 令人困惑。 以下是可重复的:

 public static IEnumerable TakeVoices( this IEnumerable voices, int needed) { int count = 0; foreach (Foo voice in voices) { if (count >= needed) yield break; yield return voice; count += voice.Voices; } } .... foreach(var voice in sample.TakeVoices(numberNeeded)) { ... } 

如果你需要,你当然可以写一个带有lambda的可重用扩展方法。

我怀疑修改TakeWhile中的’voicesSoFar’值会导致问题。