为什么C#编译器生成单个类来捕获多个lambdas的variables?

假设我们有这样的代码:

public class Observer { public event EventHandler X = delegate { }; } public class Receiver { public void Method(object o) {} } public class Program { public static void DoSomething(object a, object b, Observer observer, Receiver r) { var rCopy = r; EventHandler action1 = (s, e) => rCopy.Method(a); EventHandler action2 = (s, e) => r.Method(b); observer.X += action1; observer.X += action2; } public static void Main(string[] args) { var observer = new Observer(); var receiver = new Receiver(); DoSomething(new object(), new object(), observer, receiver); } } 

这里action1action2已经完全分开了一组捕获的variables – rCopy是为此特别创build的。 不过,编译器只生成一个类来捕获所有内容(检查生成的IL)。 我想这是为了优化的原因,但它允许非常难以察觉的内存泄漏错误:如果ab被捕获在单个类中,GC至less只要引用任何 lambda就不能收集。

有没有办法说服编译器产生两个不同的捕获类? 或者有什么理由不能做?

PS在我的博客中更详细一些: 这里和这里 。

您已经重新发现了在C#中实现匿名函数的一个已知的缺点。 我在2007年的博客中描述了这个问题。

有没有办法说服编译器产生两个不同的捕获类?

没有。

或者有什么理由不能做?

没有理论上的原因,为什么一个改进algorithm分割闭合variables,以便他们被提升到不同的闭包类别无法devise。 由于实际原因,我们还没有这样做:algorithm复杂,testing成本昂贵,而且我们一直有更高的优先级。 希望这将改变在罗斯林,但我们没有保证。

我相当肯定你在编译器的代码重写逻辑中看到了实际的限制,这不是一件容易的事情。 解决方法很简单,在一个单独的方法中创buildlambda,以便获得隐藏类的两个单独实例:

 public static void DoSomething(object a, object b, Observer observer, Receiver r) { var rCopy = r; observer.X += register(r, a); observer.X += register(rCopy, b); } private static EventHandler register(Receiver r, object obj) { return new EventHandler((s, e) => r.Method(obj)); }