我被委托清理代码中的内存泄漏,并进行检查以防止进一步的泄漏。我注意到未分离的处理程序似乎是主要原因。大多数都是直接的,但代码中有一些东西让我挠头。
第一:
myObject.someEvent -= null;
我认为这完全没有作用,这是正确的吗?(我知道,如果一个事件是本地的,你可以将其设置为null,因为它本质上是一个多播委托)。
第二,对于匿名处理程序:
myObject.someEvent += ()=> { x + y; };
myObject.someEvent -= ()=> { x + y; };
我说第二条指令也毫无价值,这是正确的吗?因为匿名方法将被编译为两个独立的委托,因此减法实际上并没有指向需要删除的正确处理程序?(对于任何正在寻找解决此问题的适当解决方案的人,请查看此处)。
我不想满足于"是的,没错",我想知道为什么这些东西不起作用(假设我的断言是正确的)。
来自文档:
需要注意的是,如果使用匿名函数订阅某个事件,则无法轻松取消订阅。要在这种情况下取消订阅,需要返回订阅该事件的代码,将匿名方法存储在委托变量中,然后将委托添加到事件中。通常,如果您在代码的稍后某个时间点必须取消订阅事件,我们建议您不要使用匿名函数订阅事件。
所以你是对的,你不能像那样删除匿名方法。以类似的方式,说myObject.someEvent -= null;
也不会做任何事情
在第一种情况下,我们可以从MulticastDelegate.CombineImpl
的反编译实现(使用IL Spy或其他东西)中看到,如果传入的委托是null
,则不会进行组合-因此,是的,删除空委托不会起任何作用。
在第二种情况下,这完全取决于编译器是否认为这两个lambda表达式相等。这个确切的问题在这篇博客文章中得到了间接的回答
在C#中,如果您有:
Func<int, int> f1 = (int x)=>x + 1; Func<int, int> f2 = (int x)=>x + 1; bool b = object.ReferenceEquals(f1, f2);
<lt;snip>>因此,在C#中,这将由实现定义;编译器可以自行决定是否使其引用相等。
您可以很容易地确定当前的C#编译器是否认为这两者相等,但这并不是真正的重点-这是实现定义的行为,不应该依赖。
第二条指令也毫无价值,因为匿名方法将被编译为两个独立的委托,因此减法实际上并没有指向需要删除的正确处理程序?
正确。每个lambda创建一个新的委托对象,该对象将不等于第一个。
如果不是的话,我希望第一个投;没有操作(我看不出它怎么能做任何有用的事情。)
似乎即使添加也无关紧要:
public Action del;
void Main()
{
del+=(()=>"1".Dump());
del+=null;
del+=null;
del+=null;
del+=(()=>"2".Dump());
del();
del.GetInvocationList().Select(f=>f.Target);
}
//ouput:
1
2