分离一个空的事件处理程序

3
我被委托清理我们代码中的内存泄漏,并添加检查以防止进一步泄漏。 我注意到未分离处理程序似乎是主要原因。 大多数都很简单,但是有几个代码中的东西让我不知所措。
首先: myObject.someEvent -= null; 我是否正确地认为这根本什么也没做?(我知道如果事件是本地的,则可以将其设置为null,因为它本质上是多播委托)。
其次,对于匿名处理程序: myObject.someEvent += ()=> { x + y; }; myObject.someEvent -= ()=> { x + y; }; 我是否正确地说第二条指令也是无用的,因为匿名方法将编译为两个单独的委托,因此减法实际上并没有指向需要删除的正确处理程序? (对于任何寻找解决此问题的正确解决方案的人,请在此处查看)。
我不想就“是的,没错”而言罢了,我想知道为什么这些事情不起作用(假设我的断言是正确的)。
4个回答

3
文档中可以看出,如果您使用匿名函数订阅事件,那么无法轻松取消订阅。在这种情况下取消订阅需要返回到订阅事件的代码中,将匿名方法存储在委托变量中,然后将该委托添加到事件中。一般来说,如果您将来需要取消订阅事件,则建议不要使用匿名函数来订阅事件。
因此,您是正确的,不能像那样删除匿名方法。同样地,myObject.someEvent -= null;也不会起作用。

1
退订的问题实际上在于它们被*相同处理。如果没有变量指向匿名方法,编译器决定新匿名函数相等的行为是未定义的,正如@Justin所指出的那样。这意味着移除将不知道由处理程序包含的匿名方法与其试图移除的方法相同。 - NominSim

2
在第一种情况下,我们可以从反编译实现的MulticastDelegate.CombineImpl中看到(使用IL Spy或其他工具),如果传入的委托是null,则不进行组合 - 因此,删除null委托不起作用。
在第二种情况下,一切都取决于编译器是否认为这两个lambda表达式相等。这个确切的问题间接地在这篇博客文章中得到了回答。

in C# if you have:

Func<int, int> f1 = (int x)=>x + 1;
Func<int, int> f2 = (int x)=>x + 1;
bool b = object.ReferenceEquals(f1, f2);

<< snip >> In C# this is therefore left to be implementation-defined; a compiler can choose to make them reference equal or not at its discretion.

你可以轻松地确定当前的C#编译器是否认为这两个是相同的,但这不是关键点——这是实现定义的行为,不能依赖它。


这真的很有趣,我从未意识到你可以用这种方式定义实现。谢谢! - Devin
1
@Devin,“实现定义”是指由编译器的实现定义 - 你(作为应用程序作者)无法定义此实现,你只能使用编译器选择的内容。 - Justin

0

看起来即使是添加也没有关系:

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

0
“第二条指令也是无用的,因为匿名方法将被编译为两个单独的委托,因此减法实际上并没有指向需要被删除的正确处理程序?”

正确。每个 lambda 都会创建一个新的委托对象,它不会等于第一个。

如果第一个不是 no-op(我看不出它如何有任何有用的作用),我希望它抛出异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接