考虑以下C#类:
c1 {
event EventHandler someEvent;
}
如果对于的事件有很多个订阅,我想要清除它们所有,最好的方法是什么? 请考虑到订阅此事件可能是匿名委托/lambda表达式。
目前我的解决方案是添加一个方法到,将设置为null。 我不知道这是否会有任何未预料到的后果。在类中,您可以将(隐藏的)变量设置为 null。空引用是表示空调用列表的规范方式,实际上。
从类外面,您不能这样做 - 事件基本上公开了“订阅”和“取消订阅”,仅此而已。
值得注意的是,字段事件实际上正在做什么 - 它们同时创建一个变量和一个事件。在类内部,您最终会引用该变量。从外部,您引用事件。
有关更多信息,请参见我的有关事件和委托的文章。
EventHandlerList
,那么你可能可以这样做。不过你需要识别出这两种情况,而且还有可能存在其他任意数量的实现方式。 - Jon Skeet为c1添加一个方法,将'someEvent'设置为null。
public class c1
{
event EventHandler someEvent;
public ResetSubscriptions() => someEvent = null;
}
class c1
{
event EventHandler someEvent;
ResetSubscriptions() => someEvent = delegate { };
}
使用delegate { }
而不是null
可以避免空引用异常。
List.Clear()
和 myList = null
的区别。 - AustinWBryansomeEvent?.Invoke(...)
,我不确定这是否很重要,或者使用noop委托会稍微缺乏清晰度是否值得。 - StayOnTargetpublic void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
// Then you will find SomeEvent is set to null.
}
public delegate string TableNameMapperDelegate(Type type);public static TableNameMapperDelegate TableNameMapper;
? - Kiquenet概念性扩展无聊评论。
我更喜欢使用“事件处理程序”而不是“事件”或“委托”。并将“事件”用于其他内容。在某些编程语言(VB.NET,Object Pascal,Objective-C)中,“事件”被称为“消息”或“信号”,甚至具有“消息”关键字和特定的语法糖。
const
WM_Paint = 998; // <-- "question" can be done by several talkers
WM_Clear = 546;
type
MyWindowClass = class(Window)
procedure NotEventHandlerMethod_1;
procedure NotEventHandlerMethod_17;
procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
procedure DoClearEventHandler; message WM_Clear;
end;
为了响应那个“消息”,需要一个“事件处理程序”来响应,无论是单个代理还是多个代理。
总结: “事件”是“问题”,“事件处理程序(们)”是答案。
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
GetInvocationList
时,如果事件为null,则您的代码会抛出异常。 - Servy这是我的解决方案:
public class Foo : IDisposable
{
private event EventHandler _statusChanged;
public event EventHandler StatusChanged
{
add
{
_statusChanged += value;
}
remove
{
_statusChanged -= value;
}
}
public void Dispose()
{
_statusChanged = null;
}
}
Dispose()
或使用 using(new Foo()){/*...*/}
模式来取消订阅委托列表中的所有成员。不要手动添加和删除回调函数,也不要在各处声明一堆委托类型:
// The hard way
public delegate void ObjectCallback(ObjectType broadcaster);
public class Object
{
public event ObjectCallback m_ObjectCallback;
void SetupListener()
{
ObjectCallback callback = null;
callback = (ObjectType broadcaster) =>
{
// one time logic here
broadcaster.m_ObjectCallback -= callback;
};
m_ObjectCallback += callback;
}
void BroadcastEvent()
{
m_ObjectCallback?.Invoke(this);
}
}
public class Object
{
public Broadcast<Object> m_EventToBroadcast = new Broadcast<Object>();
void SetupListener()
{
m_EventToBroadcast.SubscribeOnce((ObjectType broadcaster) => {
// one time logic here
});
}
~Object()
{
m_EventToBroadcast.Dispose();
m_EventToBroadcast = null;
}
void BroadcastEvent()
{
m_EventToBroadcast.Broadcast(this);
}
}
public delegate void ObjectDelegate<T>(T broadcaster);
public class Broadcast<T> : IDisposable
{
private event ObjectDelegate<T> m_Event;
private List<ObjectDelegate<T>> m_SingleSubscribers = new List<ObjectDelegate<T>>();
~Broadcast()
{
Dispose();
}
public void Dispose()
{
Clear();
System.GC.SuppressFinalize(this);
}
public void Clear()
{
m_SingleSubscribers.Clear();
m_Event = delegate { };
}
// add a one shot to this delegate that is removed after first broadcast
public void SubscribeOnce(ObjectDelegate<T> del)
{
m_Event += del;
m_SingleSubscribers.Add(del);
}
// add a recurring delegate that gets called each time
public void Subscribe(ObjectDelegate<T> del)
{
m_Event += del;
}
public void Unsubscribe(ObjectDelegate<T> del)
{
m_Event -= del;
}
public void Broadcast(T broadcaster)
{
m_Event?.Invoke(broadcaster);
for (int i = 0; i < m_SingleSubscribers.Count; ++i)
{
Unsubscribe(m_SingleSubscribers[i]);
}
m_SingleSubscribers.Clear();
}
}