C#中的闭包会导致代码膨胀吗?

5

C#中的闭包是否会导致生成的IL代码膨胀?有人告诉我要避免使用带有闭包变量的lambda表达式,因为它们会在对象文件中生成隐藏的类,可以存储lambda表达式的上下文。对于每个带有闭包变量的lambda表达式都会生成一个类。这是否属实?或者编译器是否重用现有的类,如Tuple或某些内部类?


3
那又怎样呢?你认为那会对你产生什么影响?只有在了解权衡利弊的情况下,这个问题才有意义。 - David Heffernan
2
迭代器块怎么样?yield关键字呢?自动实现的属性呢?using语句呢?它们都会产生生成的代码 - 你会称它们为臃肿吗? - Oded
是的,与简单的方法调用相比,它们很臃肿。没有代码重用。但是无论如何,消耗半个千字节的虚拟内存来存储数据不需要任何努力。半个千字节的代码需要大量的努力。你几乎肯定在优化错误的东西。 - Hans Passant
2个回答

20

只有在需要时,才会生成额外的类 - 当您捕获除this之外的变量时。然而,在大多数情况下,这并不是代码膨胀 - 它是必要的,以使委托按您需要的方式工作。

某些情况下,您可以自己编写更有效率的代码,但通常为了获得具有相同效果的委托,您将编写与编译器为您生成的代码类似但更难读的代码。

大多数情况下,您不应该担心这种"膨胀" - 首先避免为性能微调 - 优化可读性,并测量性能而不是猜测它。然后,当您证明它值得时,您可以攻击真正重要的代码部分,也许会为性能牺牲一点可读性。

(编写现代 C# 并故意避免使用 lambda 表达式就像是绑着一只手编程。如果建议您的人担心闭包的"膨胀",您可能会向他展示在 C# 5 中为异步/等待生成的状态机,从而使他感到心脏病发作...)


你能详细说明一下为什么闭合此操作不会导致生成一个类吗?另外,如果在同一个方法中有多个闭合相同变量的lambda表达式,编译器是否会共享一个单独生成的类,供每个lambda表达式之间共用? - Jeremy Bell
此外,这个问题是针对一个有数十万行代码的成熟代码库,并且需要在5-6年前的机器上平稳运行。 - Jeremy Bell

4

是的,这是真的。

需要跟踪变量的类必须存在。一个Tuple或内部类无法为所有可能的代码路径做到这一点,因此对于每个lambda/closure需要在IL中特别生成这样的状态机。

是否对您的应用程序造成问题取决于您自己来确定。


2
不,这并不是 lambda 表达式的状态机。它只是一个包含捕获变量当前状态和委托方法的类。而 迭代器块 才会生成真正的状态机。 - Jon Skeet
1
@JonSkeet - 感谢您做出区分。回答已经修改。 - Oded
对于局部变量和实例变量,不会创建新的类。请参见http://blog.sandeeprawat.com/2012/05/lambda-closure-and-compiler-generated.html。 - Ryan Vice

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接