C# 循环内 lambda 的底层代码生成

标签 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 => item.Contains(keyword)) 并传入复制关键字当前值。

为什么它不这样做?

编辑:好吧,也许我不清楚。我理解闭包何时以及如何生成,但是我不明白为什么它是这样完成的。当然,如果编译器检测到正在使用循环变量,那么为什么它不能生成临时变量并最终导致与filteredList1相同的情况,这是有道理的。我在这里错过了什么吗? 可能在某些情况下,您希望多次将相同的上下文传递给 lambda,但即使如此,当将循环变量用作上下文时,编译器使用局部变量来存储循环变量的值总是有意义的。一个 lambda。

引用 Jon Skeets 对闭包的定义“简单地说,闭包允许您封装某些行为,像任何其他对象一样传递它,并且仍然可以访问它们首次声明的上下文。”

当然,你们可以看到,c# 循环变量闭包丢失了首次设置它的循环变量的上下文。

附注请原谅我的胡言乱语,我得了严重的流感,想要简明扼要是非常困难的:-)

最佳答案

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

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

关于C# 循环内 lambda 的底层代码生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4759776/

相关文章:

c# - .NET decimal.Negate 与乘以 -1

C# 属性网格字符串编辑器

linq - linq/lambda 中的多行 foreach 循环

java - Java 8 支持闭包吗?

c# - .NET 应用程序中找不到名为 "XX.dll"的后备文化 "en-US"的附属程序集错误

c# - 如何从 DateTime.Now 生成前 6 个月的列表

c# - 使用 lambda 表达式时,c# 中的闭包如何工作?

swift - Swift 中 auto 和 escaping 闭包的区别和目的是什么?

rust - 在Rust中,我创建了一个impl返回类型,可以将其作为参数传递。但是我没有找到将其存储在Struct中的方法吗?

ios - .包含闭包: 'Bool' is ambiguous without more context [Solution: Optional String issue]