C# 闭包中的 Lambda 表达式是啥?

Posted

技术标签:

【中文标题】C# 闭包中的 Lambda 表达式是啥?【英文标题】:Are Lambda expressions in C# closures?C# 闭包中的 Lambda 表达式是什么? 【发布时间】:2012-03-24 09:22:15 【问题描述】:

lambda 表达式(在某种程度上,匿名函数)是闭包吗?

我对闭包的理解是,它们是被视为对象的函数,这似乎是匿名函数和 Lambda 表达式所做工作的准确表示。

将它们称为闭包是否正确?我知道闭包的出现(或流行)是由于 lisp 方言,但它也是一个通用的编程术语吗?

感谢您提供的任何澄清!

【问题讨论】:

注意它们被称为闭包。 “Clojure”是一种语言(因此是我的编辑)。 啊 - 这绝对消除了这种困惑!谢谢 闭包是 lambda 表达式的一个方面。 Lambda 不一定支持闭包。有些语言以不同的方式实现它。例如,Java 与 C# 的不同之处在于前者不允许修改函数内的封闭变量。也就是说,我认为这个问题是关于一般理解的,因此重复 What is the difference between a 'closure' and a 'lambda'? 【参考方案1】:

可以使用闭包来实现 lambda,但它本身不一定是闭包。

closure 是“一个函数以及该函数的非局部变量的引用环境。”。

当您创建一个使用在方法外部定义的变量的 lambda 表达式时,必须使用闭包来实现 lambda。例如:

int i = 42;

Action lambda = () =>  Console.WriteLine(i); ; 

在这种情况下,编译器生成的方法必须能够访问在完全不同的范围内定义的变量 (i)。为了让它工作,它生成的方法是一个“与引用环境一起的函数” - 基本上,它正在创建一个“闭包”来检索对变量的访问。

但是,这个 lambda:

Action lambda2 = () =>  Console.WriteLine("Foo"); 

不依赖任何“引用环境”,因为它是一个完全包含的方法。在这种情况下,编译器会生成一个普通的静态方法,完全不涉及闭包。

在这两种情况下,lambda 都会创建一个delegate(“函数对象”),但它只是在第一种情况下创建一个闭包,因为 lambda 不一定需要“捕获”所有引用环境案例。

【讨论】:

var lambda2 = () => Console.WriteLine("Foo"); 不依赖任何“引用环境”,因为它是一个完全包含的方法。它关闭了 IO 系统! :) @DanielEarwicker 我不会将 .NET 引用视为关闭系统。您设置函数运行的操作环境,而不是专门为函数本身创建环境(随函数一起传递)。有区别。 这只是一个半严肃的评论——是的,C# 编译器不需要生成一个特殊的类来表示一个闭包。但一般而言,闭包是指函数依赖于未在形式参数中传递的某个值的任何情况。从这个意义上说,即使是一个简单的 C 程序,带有一个引用全局变量的函数,也是闭包的一个例子。 请注意,您不能在 C# 中将“var”与 lambdas 一起使用,因为 lambdas 是无类型的。 我将第二个示例视为一个退化案例,但仍然是一个闭包。您的定义说闭包是“一个函数以及该函数的非局部变量的引用环境。”。在此示例中,没有非局部变量,因此作为实现细节,不需要打包环境信息。【参考方案2】:

里德的回答是正确的;我只想添加一些额外的细节:

lambda 表达式匿名方法都具有闭包语义;也就是说,它们“捕获”了它们的外部变量并延长了这些变量的生命周期。

匿名函数是我们在表示lambda 表达式或匿名方法时使用的术语。是的,这令人困惑。对不起。这是我们能想到的最好的了。

可以被视为对象的函数只是一个委托。使 lambda 成为 闭包 的原因在于它捕获了其外部变量。

转换为表达式树的 lambda 表达式也具有闭包语义,这很有趣。我告诉你,正确地实现它是一件令人头疼的事情!

“this”被认为是“外部变量”,用于创建闭包,即使“this”不是变量。

【讨论】:

虽然this 实际上不是一个变量,但我认为出于闭包语义的目的将其视为一个变量并不奇怪。它的行为与只读“变量”的行为非常相似。同样,我认为它直观地是闭包旨在捕获的上下文的一部分。最后,这是一个错误(虽然看起来很常见)假设闭包捕获值并没有太大破坏性的例子:this 事后肯定不会改变! @dlev:引用类型中的“this”不足为奇。在值类型中,有时人们会利用“this”在逻辑上是包含结构值的变量的别名这一事实;在可变值类型的方法中关闭“this”会产生意想不到的结果。【参考方案3】:

是的。闭包通常从外部范围捕获变量。 Lambda 可以做到这一点。但是,如果您的 lambda 没有捕获任何内容,则它不是闭包。

【讨论】:

按照这个定义,不带外部变量的 lambda 是否会成为闭包? Lambdas 可以做到这一点。杰森的回答更准确。 “闭包”这个词的定义可能很好,但在实践中它的使用很松散。我认为不能肯定地说什么都不捕获的 lambdas 不是闭包。 @usr 可能使用松散,因为它的定义不是很好理解。所以,它真的只是被错误地使用了 @usr - 滥用该术语的一个很好的例子是早期(前五年!)讨论将 lambdas 添加到 Java。新语法被称为“闭包”功能。最近,他们改用 lambda 来称呼它,承认这是一个正确的术语。它可能涉及也可能不涉及关闭,具体取决于 lambda 主体所说的内容。【参考方案4】:

这是“闭包”而不是“clojure”。

这不是闭包。闭包基本上是一个函数的表示,以及该函数使用的任何非局部变量。

从这个意义上说,lambda 不是闭包,但如果它们关闭任何变量,它们确实会导致编译器生成闭包。

如果您在包含关闭某些变量的 lambda 的程序集上使用 ILDASM,您将在该程序集中看到编译器生成的类,该类代表函数和关闭的变量。那闭包。

当你说

被视为对象的函数,

这通常只是“函数对象”(在 C# 中我们会说“委托”)并且在函数式编程中很常见。

【讨论】:

以上是关于C# 闭包中的 Lambda 表达式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

C#中的闭包和意想不到的坑

java中的lambda表达式是啥?

关于 C# 闭包

正确使用和理解C#中的闭包

lambada表达式

groovy 中的闭包与 java 8 中的闭包(lambda 表达式)?