循环中lambda的引擎代码生成下的C#

时间:2011-01-21 14:13:35

标签: c# lambda closures

这个问题是基于我最喜欢的海报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。请原谅我有严重流感的莫名其妙,并试图简明扼要是非常困难的: - )

2 个答案:

答案 0 :(得分:3)

你必须记住,clousures关闭变量,而不是值...所以在每次迭代时,对于第一个过滤器,你在var'value'中捕获关键字值,这正是你所期望的;但是对于第二个过滤器,你捕获迭代变量'keyword',所以当它被执行时,所有过滤器都有相同的关键字值(迭代中的最后一个关键字)'world',它正确地显示了两个条目包含'世界'

查看以下问题以获得解释

Why is it bad to use an iteration variable in a lambda expression

答案 1 :(得分:3)

我认为关键是要理解两件事:

  1. 何时以及如何关闭 产生的?在这种情况下第一个 闭包是一个临时变量 叫value,第二个叫。{ 循环变量
  2. 什么时候lambda包含 关闭执行?这两个lambdas 在最后foreach执行 循环。到目前为止他们只有 已组装,但未执行。 现在他们每个人都被执行了 他们检查了它的价值 关闭,第一次使用了 临时变量是 不同的每一步 foreach循环(因为它每次都是一个新变量)。第二个是使用 无论最后值是什么 foreach循环变量就是它的时候 建成了。