关于精确定义,建议查看维基百科条目。它非常好。我只是想用一个例子来澄清。
假设这是一段C#代码片段(旨在在列表中执行AND
搜索):
List<string> list = new List<string> { "hello world", "goodbye world" };
IEnumerable<string> filteredList = list;
var keywords = new [] { "hello", "world" };
foreach (var keyword in keywords)
filteredList = filteredList.Where(item => item.Contains(keyword));
foreach (var s in filteredList)
Console.WriteLine(s);
在C#中,这是一个常见的陷阱。如果你看一下Where
里面的lambda表达式,你会发现它定义了一个函数,其行为取决于其定义处变量的值。就像把变量本身传递给函数,而不是变量的值。实际上,当调用此闭包时,它检索keyword
变量在该时间的值。这个示例的结果非常有趣。它打印出两个"hello world"和"goodbye world",这不是我们想要的。发生了什么?正如我上面所说,我们使用lambda表达式声明的函数是一个闭包,它对keyword
变量进行了封闭,因此发生了这种情况:
filteredList = filteredList.Where(item => item.Contains(keyword))
.Where(item => item.Contains(keyword))
在执行关闭操作时,keyword
的值为"world",因此我们基本上是使用相同的关键字多次过滤列表。解决方案是:
foreach (var keyword in keywords) {
var temporaryVariable = keyword;
filteredList = filteredList.Where(item => item.Contains(temporaryVariable));
}
由于temporaryVariable
的作用域限制在foreach
循环的主体中,因此在每次迭代中,它都是一个不同的变量。实际上,每个闭包将绑定到不同的变量(这些是每次迭代时不同实例的temporaryVariable
)。这次,它会给出正确的结果("hello world"):
filteredList = filteredList.Where(item => item.Contains(temporaryVariable_1))
.Where(item => item.Contains(temporaryVariable_2))
其中temporaryVariable_1
在闭包执行时的值为"hello",temporaryVariable_2
的值为"world"。请注意,闭包延长了变量的生命周期(它们的生命应该在循环的每次迭代后结束),这也是闭包的一个重要副作用。