参加以下C#课程:
c1 {
event EventHandler someEvent;
}
如果对c1
的{{1}}事件有很多订阅,并且我想清除它们,那么实现这一目标的最佳方法是什么? 另请注意,此活动的订阅可能是lambdas /匿名代表。
目前我的解决方案是向someEvent
添加ResetSubscriptions()
方法,将c1
设置为null。我不知道这是否有任何看不见的后果。
答案 0 :(得分:171)
在类中,您可以将(hidden)变量设置为null。空引用是有效地表示空调用列表的规范方式。
从课外,你不能这样做 - 事件基本上暴露了“订阅”和“取消订阅”,就是这样。
值得注意的是,类似字段的事件实际上是在做什么 - 他们同时创建了一个变量和一个事件。在类中,您最终引用变量。从外面,你可以参考这个活动。
有关详细信息,请参阅我的article on events and delegates。
答案 1 :(得分:28)
向c1添加一个方法,将'someEvent'设置为null ...
class c1
{
event EventHandler someEvent;
ResetSubscriptions() {someEvent = null;}
}
答案 2 :(得分:7)
class c1
{
event EventHandler someEvent;
ResetSubscriptions() {someEvent = delegate{};}
}
最好使用委托{}而不是
答案 3 :(得分:6)
在类中将事件设置为null有效。当你处理一个类时,你应该总是将事件设置为null,GC有事件问题,如果它有悬空事件,可能不会清理处理过的类。
答案 4 :(得分:6)
清除所有订阅者的最佳做法是,如果要将此功能公开给外部,可以通过添加另一个公共方法将someEvent设置为null。这没有任何看不见的后果。前提条件是记住使用关键字“event”声明SomeEvent。
请参阅书籍 - 简言之C#4.0,第125页。
这里有人建议使用Delegate.RemoveAll
方法。如果您使用它,示例代码可以遵循以下形式。但这真的很愚蠢。为什么不在SomeEvent=null
函数内部ClearSubscribers()
?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
}
答案 5 :(得分:5)
您可以使用Delegate.Remove或Delegate.RemoveAll方法来实现此目的。
答案 6 :(得分:3)
概念扩展无聊评论。
我宁愿使用“事件处理程序”这个词而不是“事件”或“委托”。其他东西使用“事件”这个词。在某些编程语言(VB.NET,Object Pascal,Objective-C)中,“event”被称为“message”或“signal”,甚至还有一个“message”关键字和特定的糖语法。
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;
并且,为了响应该“消息”,“事件处理程序”响应,无论是单个委托还是多个委托。
要点: “事件”是“问题”,“事件处理程序”是答案。
答案 7 :(得分:1)
这是我的解决方案:
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()){/*...*/}
模式取消订阅调用列表的所有成员。
答案 8 :(得分:0)
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
答案 9 :(得分:-1)
不是手动添加和删除回调,而是在各处声明一堆委托类型:
// 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();
}
}