40

假设我们有这样的代码:

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有完全分离的一组捕获变量 -rCopy是专门为此创建的。尽管如此,编译器只生成一个类来捕获所有内容(检查生成的 IL)。我想这样做是出于优化的原因,但它允许非常难以发现的内存泄漏错误:如果ab在单个类中捕获,则至少只要引用了任何lambda,GC 就无法收集两者。

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

PS 更详细一点,在我的博客中:这里这里

4

2 回答 2

35

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

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

不。

或者有什么理由不能完成?

没有理论上的理由为什么不能设计一种改进的算法来划分封闭变量,以便将它们提升到不同的封闭类中。出于实际原因,我们没有这样做:算法很复杂,正确和测试成本很高,而且我们一直有更高的优先级。希望罗斯林的情况会有所改变,但我们不做任何保证。

于 2012-08-17T18:01:51.430 回答
27

我相当确定您在编译器的代码重写逻辑中看到了实际限制,这并不容易。解决方法很简单,在单独的方法中创建 lambda,以便获得隐藏类的两个单独实例:

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));
}
于 2012-08-17T16:50:19.687 回答