闭包和传统类有啥区别?
Posted
技术标签:
【中文标题】闭包和传统类有啥区别?【英文标题】:What's the difference between closures and traditional classes?闭包和传统类有什么区别? 【发布时间】:2011-03-23 01:47:24 【问题描述】:闭包对类的优缺点是什么,反之亦然?
编辑: 正如用户 Faisal 所说,闭包和类都可以用来“描述一个维护和操作状态的实体”,因此闭包提供了一种使用函数式语言以面向对象的方式进行编程的方法。像大多数程序员一样,我更熟悉类。
这个问题的目的不是要引发另一场关于哪种编程范式更好,或者闭包和类是否完全等效,或者穷人彼此的激烈战争。
我想知道的是,是否有人发现一种方法真正胜过另一种方法,以及为什么。
【问题讨论】:
以与语言无关的方式回答这个问题非常非常困难,因为最方便的方法几乎必然取决于您使用的语言。 【参考方案1】:闭包与类的关系非常轻微。类允许您定义字段和方法,而闭包保存有关来自函数调用的局部变量的信息。不可能以与语言无关的方式对两者进行比较:它们根本没有达到相同的目的。此外,与面向对象编程相比,闭包与函数式编程的相关性要高得多。
例如,看下面的 C# 代码:
static void Main(String[] args)
int i = 4;
var myDelegate = delegate()
i = 5;
Console.WriteLine(i);
myDelegate();
Console.WriteLine(i);
这给出“4”然后是“5”。 myDelegate
,作为一个委托,是一个闭包,知道函数当前使用的所有变量。因此,当我调用它时,允许在“父”函数内部更改i
的值。这对于正常功能是不允许的。
类,如果你知道它们是什么,它们是完全不同的。
您感到困惑的一个可能原因是,当一种语言不支持闭包时,可以使用包含我们需要保留的每个变量的类来模拟它们。例如,我们可以像这样重写上面的代码:
class MainClosure()
public int i;
void Apply()
i = 5;
static void Main(String[] args)
MainClosure closure;
closure.i = 4;
Console.WriteLine(closure.i);
closure.Apply();
Console.WriteLine(closure.i);
我们已将delegate
转换为我们称之为MainClosure
的类。我们没有在Main
函数中创建变量i
,而是创建了一个MainClosure
对象,它有一个i
字段。这是我们将使用的。此外,我们已经在实例方法内部而不是在方法内部构建了函数执行的代码。
如您所见,尽管这是一个简单的示例(只有一个变量),但工作量要大得多。在需要闭包的上下文中,使用对象是一个糟糕的解决方案。然而,类不仅对创建闭包有用,而且它们通常的用途也大不相同。
【讨论】:
我也这么认为,但是对于闭包或对象是否更具表现力,显然存在相当多的争论。让我给你一点讨论的味道:people.csail.mit.edu/gregs/ll1-discuss-archive-html/… 根据我的理解,可以通过闭包来实现对象,也可以通过对象来模拟闭包。但是,对象通常不会自动捕获本地环境,因此您必须显式初始化它们......但@zneak 是正确的:它们最终服务于两个不同的目的,并且想要两者都不是不合理的。 @Faisal:我相信这场辩论是关于明确使用闭包还是创建一个类来模拟闭包,在这种情况下,“成熟”的闭包显然更方便。 @Amadan, @zneak,使闭包成为对象超集的基本属性是什么? @zneak:如果闭包可用作语言结构,为什么还要使用类来模拟闭包?不过,我同意你得出的结论是显而易见的。 :)【参考方案2】:在功能上,闭包和对象是等价的。闭包可以模拟一个对象,反之亦然。因此,您使用哪一种是语法方便的问题,或者您的编程语言可以最好地处理哪一种。
在 C++ 中,闭包在语法上不可用,因此您不得不使用“函子”,它们是覆盖 operator()
的对象,并且可能以看起来像函数调用的方式被调用。
在 Java 中,你甚至没有函子,所以你会得到类似访问者模式的东西,它只是支持闭包的语言中的高阶函数。
在标准的 Scheme 中你没有对象,所以有时你最终通过编写一个带有调度函数的闭包来实现它们,根据传入的参数执行不同的子闭包。
在像 Python 这样的语言中,它的语法既有函子又有闭包,这基本上是一个品味问题,你认为哪种方式是表达你正在做的事情的更好方式。
就我个人而言,我想说的是,在任何同时具有这两种语法的语言中,闭包都是一种用单一方法表达对象的更清晰、更简洁的方式。反之亦然,如果您的闭包开始根据传入参数处理对子闭包的分派,则您可能应该改用对象。
【讨论】:
【参考方案3】:就我个人而言,我认为这是一个使用正确工具来完成工作的问题......更具体地说,是正确传达你的意图。
如果您想明确显示所有对象共享一个公共定义并希望对此类进行强类型检查,您可能需要使用一个类。在这种情况下,无法在运行时更改类结构的缺点实际上是一种优势,因为您确切地知道自己在处理什么。
如果您想创建一个异构的“对象”集合(即,状态表示为在某些函数下关闭的变量,其中包含用于操作该数据的内部函数),您最好创建一个闭包。在这种情况下,您最终得到的对象的结构并没有真正的保证,但您可以在运行时完全按照自己的喜好进行定义。
实际上,谢谢您的提问;我会用一种下意识的方式回应“课程和关闭是完全不同的!”起初的态度,但通过一些研究,我意识到问题并没有我想象的那么简单。
【讨论】:
换句话说,作为对象的闭包更像是大多数 OOP 语言中的 接口——内部表示是完全隐藏的,任何提供相同公共方法的东西都可以换了。有些人实际上认为这是更好的 OOP 风格,有趣的是......以上是关于闭包和传统类有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章