基本前提:
我有一个房间,当一个阿凡达“进入”房间内的所有头像时,它会发布一个事件。当“阿凡达”离开房间时,我希望它删除该房间的所有订阅。
在将头像添加到新房间并订阅新房间的活动之前,我怎样才能最好地从房间内的所有活动中取消订阅“阿凡达”?
代码如下:
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");
}
}
答案 0 :(得分:50)
每个委托都有一个名为GetInvocationList()
的方法,它返回已注册的所有实际委托。因此,假设委托类型(或事件)名为MyDelegate
,并且处理程序实例变量名为myDlgHandler
,则可以写:
Delegate[] clientList = myDlgHandler.GetInvocationList();
foreach (var d in clientList)
myDlgHandler -= (d as MyDelegate);
涵盖可能为null的情况,
if(myDlgHandler != null)
foreach (var d in myDlgHandler.GetInvocationList())
myDlgHandler -= (d as MyDelegate);
答案 1 :(得分:4)
标准删除有什么问题吗?
public void RemovePlayer(Avatar theAvatar) {
AvatarEntersRoom -= new EventHandler<EnterRoomEventArgs>(theAvatar.HandleAvatarEntersRoom);
}
修改强>
根据您的更新,您似乎希望代码能够从特定类的所有事件中删除特定对象。没有现实的方法来实现这一目标。它通常有点冗长,但最好的方法是从每个事件中单独添加/删除特定的对象方法组合。
关闭此功能的唯一方法是使用反射。你可以反思地抓住你班上的所有事件,然后做一些魔术来找到事件链中所有类的实例。这只是一个部分解决方案,因为它会忽略诸如lambda表达式事件处理程序之类的东西。
答案 2 :(得分:4)
实现此目标的最简单方法可能是将所有订阅的事件存储在事件的ArrayList
个代表中。
当头像离开房间时,只需循环执行标准删除(-=
)的代表列表。
答案 3 :(得分:1)
您可以使用以下命令在所有活动订阅者上运行:
_Event.GetInvocationList()
并删除每个事件处理程序。
Delegate[] subscribers = myEvent.GetInvocationList();
for(int i = 0; i < subscribers.Length; i++)
{
myEvent -= subscribers[i] as yourDelegateType;
}
答案 4 :(得分:0)
我想做的是在调试中(不要认为这对于发布来说是一个很好的性能明智的选择,并且应该在开发过程中捕获它)在没有取消订阅类的事件时引发异常,这是我使用的方法:
def dec(fn):
return fnB
class A:
@dec
def a(self):
pass
A().a()
>>> <__main__.A object at 0x1051ba4a8>
print(A().a)
>>> <bound method fnB of <__main__.A object at 0x10d44e4a8>>
Works Great!! fnB has became bound method fnB But...
def dec(fn):
return callable_b
class A:
@dec
def a(self):
pass
A().a()
TypeError: __call__() missing 1 required positional argument: 'self_from_class_a'
print(A().a)
>>> <__main__.clsB object at 0x10f372470>
在“我的处置”中拥有委托的项目,或者工作流应该释放该对象的任何其他位置,我都会这样称呼它。
#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
然后很容易找到这些“孤立”事件的订户并修复代码
ps: 这种“练习模式”的扩展看起来像这样。
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;
}
}
}