为什么C#编译器生成单个类来捕获几个lambdas的变量?

Iva*_*lov 40 c#

假设我们有这样的代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里action1并且action2完全分离了一组捕获的变量 - rCopy特别为此创建.仍然,编译器只生成一个类来捕获所有内容(检查生成的IL).我想这是优化的原因做了,但它允许非常难以现货内存泄漏缺陷:如果ab单类捕获,GC是无法同时收集至少只要任何 lambda表达式的引用.

有没有办法说服编译器生成两个不同的捕获类?或者为什么不能这样做?

PS在我的博客中有点更详细:这里这里.

Eri*_*ert 35

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

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

没有.

或者为什么不能这样做?

没有理论上的理由可以解释为什么不能设计用于分割闭合变量的改进算法以便将它们提升到不同的闭包类中.我们还没有出于实际原因这样做:算法复杂,成本高昂,测试成本高,而且我们总是有更高的优先级.希望罗斯林会改变,但我们不保证.


Han*_*ant 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));
}
Run Code Online (Sandbox Code Playgroud)

  • 此时,是否需要Receiver副本?你能不能在两种方法中使用原始的Receiver,而不是使用Implicit捕获? (3认同)