如何取消订阅 C# 中特定类的事件的所有处理程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/447821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I unsubscribe all handlers from an event for a particular class in C#?
提问by Nathan Smith
Basic premise:
基本前提:
I have a Room which publishes an event when an Avatar "enters" to all Avatars within the Room. When an Avatar leaves the Room I want it to remove all subscriptions for that room.
我有一个房间,当一个头像“进入”房间内的所有头像时,它会发布一个事件。当头像离开房间时,我希望它删除该房间的所有订阅。
How can I best unsubscribe the Avatar from all events in the room before I add the Avatar to a new Room and subscribe to the new Room's events?
在将 Avatar 添加到新 Room 并订阅新 Room 的事件之前,如何最好地从 Room 中的所有事件取消订阅 Avatar?
The code goes something like this:
代码是这样的:
class Room
{
public event EventHandler<EnterRoomEventArgs> AvatarEntersRoom;
public event EvnetHandler<LeaveRoomEventArgs> AvatarLeavesRoom;
public event EventHandler<AnotherOfManyEventArgs> AnotherOfManayAvatarEvents;
public void AddPlayer(Avatar theAvatar)
{
AvatarEntersRoom(this, new EnterRoomEventArgs());
AvatarEntersRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AvatarLeavesRoom += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
AnotherOfManayAvatarEvents += new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
}
class Avatar
{
public void HandleAvatarEntersRoom(object sender, EnterRoomEventArgs e)
{
Log.Write("avatar has entered the room");
}
public void HandleAvatarLeaveRoom(object sender, LeaveRoomEventArgs e)
{
Log.Write("avatar has left room");
}
public void HandleAnotherOfManayAvatarEvents(object sender, AnotherOfManyEventArgs e)
{
Log.Write("another avatar event has occurred");
}
}
采纳答案by Charles Bretana
Each delegate has a method named GetInvocationList()
that returns all the actual delegates that have been registered. So, assuming the delegate Type (or event) is named say MyDelegate
, and the handler instance variable is named myDlgHandler
, you can write:
每个委托都有一个名为的方法GetInvocationList()
,该方法返回所有已注册的实际委托。因此,假设委托类型(或事件)名为 say MyDelegate
,并且处理程序实例变量名为myDlgHandler
,您可以编写:
Delegate[] clientList = myDlgHandler.GetInvocationList();
foreach (var d in clientList)
myDlgHandler -= (d as MyDelegate);
to cover the case where it might be null,
覆盖它可能为空的情况,
if(myDlgHandler != null)
foreach (var d in myDlgHandler.GetInvocationList())
myDlgHandler -= (d as MyDelegate);
回答by JaredPar
Is there anything wrong with a standard remove?
标准删除有什么问题吗?
public void RemovePlayer(Avatar theAvatar) {
AvatarEntersRoom -= new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
EDIT
编辑
Based on your update it appears that you want code that will remove a particular object from all events on a particular class. There is no realistic way to accomplish this goal. It's often a bit verbose but the best way is to individually add/remove a particular object method combo from every event.
根据您的更新,您似乎想要从特定类的所有事件中删除特定对象的代码。没有现实的方法来实现这个目标。它通常有点冗长,但最好的方法是从每个事件中单独添加/删除特定的对象方法组合。
The only way to get closeto this functionality is to use reflection. You could reflectively grab all events on your class and then do some magic to find all instances of a class within the event chain. This will only be a partial solution though because it will ignore such things as a lambda expression event handlers.
接近此功能的唯一方法是使用反射。您可以反射性地获取类中的所有事件,然后执行一些魔术以在事件链中查找类的所有实例。这只是一个部分解决方案,因为它会忽略诸如 lambda 表达式事件处理程序之类的东西。
回答by Luke
Probably the simplest way to accomplish this would be to store all of your subscribed events for an avatar in an ArrayList
of delegates to the events.
可能实现此目的的最简单方法是将所有订阅的化身事件存储ArrayList
在事件的委托中。
When the avatar leaves the room, simply loop through the list of delegates performing a standard remove (-=
).
当头像离开房间时,只需循环执行标准删除 ( -=
)的代表列表。
回答by Moran Helman
you can run on all the event subscribers with:
您可以在所有事件订阅者上运行:
_Event.GetInvocationList()
and remove each event handler.
并删除每个事件处理程序。
Delegate[] subscribers = myEvent.GetInvocationList();
for(int i = 0; i < subscribers.Length; i++)
{
myEvent -= subscribers[i] as yourDelegateType;
}
回答by Walter Vehoeven
What I'd like to do is in debug (do not think that this is good performance wise for release and one should catch it during development) throw exceptions when a class's events are not unsubscribed, this is the method that I use:
我想做的是在调试中(不要认为这是发布的良好性能,应该在开发过程中捕获它)在类的事件未取消订阅时抛出异常,这是我使用的方法:
#if DEBUG
private void CheckEventHasNoSubscribers(Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// throw an exception listing all current subscription that would hinder GC on them!
throw new Exception(
$"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}
#endif
The in my Dispose of the item that owns the delegate, or any other location where you're workflow supposed to release the object I would call it like this.
在拥有委托的项目的我的处置中,或工作流应该在其中释放对象的任何其他位置,我会这样称呼它。
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (_orderCacheLock != null)
_orderCacheLock.Dispose();
if(_SettingTradeTimeOut!=null)
_SettingTradeTimeOut.Dispose();
_orderCacheLock = null;
#if DEBUG
CheckEventHasNoSubscribers(OnIsProfitable);
CheckEventHasNoSubscribers(OnPropertyChanged);
#endif
disposedValue = true;
}
}
It's then super easy to find the subscribers to these "orphaned" events and fix the code
然后就非常容易找到这些“孤立”事件的订阅者并修复代码
ps: An Extension of this "practice pattern" looks like this.
ps:这个“练习模式”的扩展看起来像这样。
public static void CheckEventHasNoSubscribers(this Delegate eventDelegate)
{
if (eventDelegate != null)
if (eventDelegate.GetInvocationList().Length != 0)
{
var subscriberCount = eventDelegate.GetInvocationList().Length;
// determine the consumers of this event
var subscribers = new StringBuilder();
foreach (var del in eventDelegate.GetInvocationList())
subscribers.AppendLine((subscribers.Length != 0 ? ", " : "") + del.Target);
// point to the missing un-subscribed events
throw new Exception( $"Event:{eventDelegate.Method.Name} still has {subscriberCount} subscribers, with the following targets [{subscribers}]");
}
}
}