这个问题是基于我最喜欢的海报之一 Mehrdad Afshari 在 this 中给出的答案。关于关闭的问题。
我很难理解为什么 C# 会这样生成代码......
这是有问题的代码
static void Main(string[] args)
{
List<string> list = new List<string> { "hello world", "TED", "goodbye world" };
IEnumerable<string> filteredList1 = list;
IEnumerable<string> filteredList2 = list;
var keywords = new[] { "hello", "world" };
foreach (var keyword in keywords)
{
//Will execute the following
//filteredList1 = filteredList1.Where(item => item.Contains("hello")).Where(item => item.Contains("world"));;
string value = keyword;
filteredList1 = filteredList1.Where(item => item.Contains(value));
//Will execute the following
//filteredList2 = filteredList2.Where(item => item.Contains("world"))
filteredList2 = filteredList2.Where(item => item.Contains(keyword));
}
Console.WriteLine("===================================================");
Console.WriteLine("LIST 1");
foreach (var s in filteredList1) // closure is called here
Console.WriteLine(s);
Console.WriteLine("===================================================");
Console.WriteLine("LIST 2");
foreach (var s in filteredList2) // closure is called here
Console.WriteLine(s);
Console.WriteLine("===================================================");
}
}
给出以下输出
===============
LIST 1
hello world
===============
LIST 2
hello world
goodbye world
===============
我的问题是我不明白为什么filteredList2列表没有生成与filteredList1相同的代码。 似乎更明智的是,每次迭代 foreach (关键字中的 var 关键字)应该附加另一个 .Where(item => item.Contains(keyword)) 并传入复制关键字当前值。
为什么它不这样做?
编辑:好吧,也许我不清楚。我理解闭包何时以及如何生成,但是我不明白为什么它是这样完成的。当然,如果编译器检测到正在使用循环变量,那么为什么它不能生成临时变量并最终导致与filteredList1相同的情况,这是有道理的。我在这里错过了什么吗? 可能在某些情况下,您希望多次将相同的上下文传递给 lambda,但即使如此,当将循环变量用作上下文时,编译器使用局部变量来存储循环变量的值总是有意义的。一个 lambda。
引用 Jon Skeets 对闭包的定义“简单地说,闭包允许您封装某些行为,像任何其他对象一样传递它,并且仍然可以访问它们首次声明的上下文。”
当然,你们可以看到,c# 循环变量闭包丢失了首次设置它的循环变量的上下文。
附注请原谅我的胡言乱语,我得了严重的流感,想要简明扼要是非常困难的:-)
最佳答案
我认为关键是理解两件事:
- 何时以及如何关闭
生成?在这种情况下,第一个
闭包是针对临时变量的
称为
value
,第二个 循环变量 - 什么时候 lambda 包含
关闭执行?这里两个 lambda
在最后的
foreach
处执行 循环。到目前为止他们只有 已被聚集,但未被处决。 现在他们每个人都被处决了 他们检查的值(value) 闭包,第一个是使用 临时变量是 的每一步都不同 foreach 循环(因为每次都是一个新变量)。第二个是使用 无论最后值是什么 foreach 循环变量是当它 已建成。
关于C# 循环内 lambda 的底层代码生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4759776/