C# 分离空事件处理程序

C# 分离空事件处理程序,c#,memory-leaks,event-handling,C#,Memory Leaks,Event Handling,我受委托清理代码中的内存泄漏,并进行检查以防止进一步泄漏。我注意到不独立的处理人员似乎是主要原因。大多数都是直截了当的,但代码中有一些东西让我抓狂 第一: myObject.someEvent-=null 我认为这完全不起作用,对吗?(我知道如果事件是本地的,可以将其设置为null,因为它本质上是一个多播委托) 第二,对于匿名处理程序: myObject.someEvent+=()=>{x+y;} myObject.someEvent-=()=>{x+y;} 因为匿名方法将被编译为两个独立的委托

我受委托清理代码中的内存泄漏,并进行检查以防止进一步泄漏。我注意到不独立的处理人员似乎是主要原因。大多数都是直截了当的,但代码中有一些东西让我抓狂

第一:

myObject.someEvent-=null

我认为这完全不起作用,对吗?(我知道如果事件是本地的,可以将其设置为null,因为它本质上是一个多播委托)

第二,对于匿名处理程序:

myObject.someEvent+=()=>{x+y;}
myObject.someEvent-=()=>{x+y;}

因为匿名方法将被编译为两个独立的委托,因此减法实际上没有指向需要删除的正确处理程序,所以我说第二条指令也没有价值,对吗?(对于寻求解决此问题的适当解决方案的人,请查看)

我不想满足于“是的,那是对的”,我想知道为什么这些东西不起作用(假设我的断言是正确的)

似乎即使添加也不重要:

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
第二条指令也毫无价值,因为匿名方法将被编译为两个独立的委托,因此减法实际上没有指向需要删除的正确处理程序

对。每个lambda创建一个新的委托对象,该委托对象将不等于第一个委托对象

如果不是的话,我希望第一个扔出去;t无操作(我看不出它如何能做任何有用的事情。)

来自:

重要的是要注意,如果您使用匿名函数订阅活动,则无法轻松取消订阅。要在这种情况下取消订阅,必须返回订阅事件的代码,将匿名方法存储在委托变量中,然后将委托添加到事件中。一般来说,如果您必须在代码的稍后某个点取消订阅事件,我们建议您不要使用匿名函数来订阅事件


因此,您是正确的,您不能删除这样的匿名方法。以类似的方式,说
myObject.someEvent-=null
也不会执行任何操作

在第一种情况下,我们可以从
MulticastDelegate.CombineImpl
(使用IL Spy或其他工具)的反编译实现中看到,如果传入的委托为
null
,则不会执行任何组合操作-因此,是的,删除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);
Func f1=(int x)=>x+1;
Func f2=(int x)=>x+1;
bool b=对象。ReferenceEquals(f1,f2);
>在C#中,这由实现定义;编译程序 可自行决定是否使其参考值相等


您可以很容易地确定当前的C#编译器是否认为这两个函数相等或不相等,但这不是真正的问题-这是实现定义的行为,不应该依赖。

取消订阅的问题实际上是它们的处理方式相同。如果没有一个指向匿名方法的变量,@Justin指出,用于确定新匿名函数相等性的编译器行为是未定义的。这意味着删除操作不会知道处理程序包含的匿名方法与它试图删除的匿名方法相同。这真的很有趣,我从未意识到您可以用这种方式定义实现。谢谢@Devin所说的“实现定义”,我的意思是由编译器的实现定义的——您(作为应用程序的作者)不能定义这个实现,您必须执行编译器选择执行的任何操作。