采用以下代码,Resharper告诉我voicesSoFar
和voicesNeededMaximum
导致“访问修改后的闭包”。我读到了这些,但令我困惑的是Resharper建议通过在LINQ查询之前提取变量来解决这个问题。但那已经是他们的地方了!
如果我只是在int voicesSoFar1 = voicesSoFar
之后立即添加int voicesSoFar = 0
,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));
答案 0 :(得分: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;
}
}
用法:
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
答案 1 :(得分:3)
嗯,错误消息是正确的,因为在操作期间不会保留voicesSoFar
的值。在纯粹的“功能性”术语中(而lambda实际上是为了在功能上起作用),这将是令人困惑的。
例如,一个有趣的测试是:
如果我重复两次查询会怎么样?
例如:
int count = voters.Count();
var first = voters.FirstOrDefault();
我相信你可以看到... 10
,null
- 令人困惑。以下内容是可重复的:
public static IEnumerable<Foo> TakeVoices(
this IEnumerable<Foo> 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的可重用扩展方法。
答案 2 :(得分:0)
我怀疑修改TakeWhile中的'voicesSoFar'值会导致问题。