假设我们有这样的代码:
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);
}
}
此处action1
和action2
已完全分开捕获的变量集 - rCopy
是专门为此创建的。仍然,编译器只生成一个类来捕获所有内容(检查生成的IL)。我认为它是出于优化原因而完成的,但它允许非常难以发现的内存泄漏错误:如果在单个类中捕获a
和b
,则GC至少无法收集这两者引用任何 lambda。
有没有办法说服编译器生成两个不同的捕获类?或者为什么不能这样做?
答案 0 :(得分:35)
您已经重新发现了在C#中实现匿名函数的一个已知缺点。 I described the problem in my blog in 2007.
有没有办法说服编译器生成两个不同的捕获类?
没有。
或者为什么不能这样做?
没有理论理由为什么无法设计一种改进的分区闭合变量算法,以便将它们提升到不同的闭包类中。我们还没有出于实际原因这样做:算法复杂,成本高昂,测试成本高,而且我们总是有更高的优先级。希望罗斯林会改变,但我们不做任何保证。
答案 1 :(得分: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));
}