这个问题是基于我最喜欢的海报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 =&gt; item.Contains(keyword))并传入复制关键字当前值,这似乎更为明智。
为什么不这样做?
编辑:好吧也许我不清楚。我理解闭包产生的时间和方式,但我不明白为什么这样做。当编译器检测到正在使用循环变量时,它肯定是有道理的,那么为什么它不会生成临时变量并最终与filteredList1处于相同的情况。我在这里错过了什么吗? 可能存在一些情况,您希望将相同的上下文多次传递给lambda,但即使这样,编译器使用局部变量来存储循环变量的值(当它用作上下文时)总是有意义的。一个lambda。
引用Jon Skeets对闭包的定义“简单地说,闭包允许你封装一些行为,像任何其他对象一样传递它,并且仍然可以访问它们首次被声明的上下文。”
当然,你们可以看到c#closure over loop变量会丢失首次设置它的循环变量的上下文。
P.S。请原谅我有严重流感的莫名其妙,并试图简明扼要是非常困难的: - )
答案 0 :(得分:3)
你必须记住,clousures关闭变量,而不是值...所以在每次迭代时,对于第一个过滤器,你在var'value'中捕获关键字值,这正是你所期望的;但是对于第二个过滤器,你捕获迭代变量'keyword',所以当它被执行时,所有过滤器都有相同的关键字值(迭代中的最后一个关键字)'world',它正确地显示了两个条目包含'世界'
查看以下问题以获得解释
Why is it bad to use an iteration variable in a lambda expression
答案 1 :(得分:3)
我认为关键是要理解两件事:
value
,第二个叫。{
循环变量foreach
执行
循环。到目前为止他们只有
已组装,但未执行。
现在他们每个人都被执行了
他们检查了它的价值
关闭,第一次使用了
临时变量是
不同的每一步
foreach循环(因为它每次都是一个新变量)。第二个是使用
无论最后值是什么
foreach循环变量就是它的时候
建成了。