回答:
クラス内から、(非表示の)変数をnullに設定できます。null参照は、空の呼び出しリストを効果的に表す正規の方法です。
クラスの外からこれを行うことはできません。イベントは基本的に「購読」と「購読解除」を公開し、それだけです。
フィールドのようなイベントが実際に何をしているのかを知ることは価値があります-それらは同時に変数とイベントを作成しています。クラス内では、変数を参照することになります。外部から、イベントを参照します。
詳細については、イベントとデリゲートに関する私の記事を参照してください。
EventHandlerList
、あなたのことができるようになります。ただし、これらの2つのケースを認識する必要があります。他の実装もいくつもあります。
hidden
。
class c1
{
event EventHandler someEvent;
ResetSubscriptions() => someEvent = delegate { };
}
null ref例外を回避するdelegate { }
よりも、使用することnull
をお勧めします。
List.Clear()
vs を使うようなものmyList = null
です。
クラス内でイベントをnullに設定すると機能します。クラスを破棄するときは常にイベントをnullに設定する必要があります。GCはイベントに問題があり、ぶら下がりイベントがある場合、破棄されたクラスをクリーンアップしないことがあります。
すべてのサブスクライバーをクリアするためのベストプラクティスは、この機能を外部に公開する場合は、別のパブリックメソッドを追加してsomeEventをnullに設定することです。これには目に見えない結果はありません。前提条件は、キーワード 'event'を使用してSomeEventを宣言することを忘れないことです。
125ページの本-C#4.0の概要を参照してください。
ここで誰かがDelegate.RemoveAll
方法を使用することを提案しました。これを使用する場合、サンプルコードは以下のフォームに従うことができます。しかし、それは本当に愚かです。関数SomeEvent=null
内だけではないのClearSubscribers()
ですか?
public void ClearSubscribers ()
{
SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
// Then you will find SomeEvent is set to null.
}
概念的な拡張退屈なコメント。
「イベント」や「デリゲート」ではなく、「イベントハンドラ」という言葉を使用します。そして、「イベント」という言葉を他のものに使用しました。一部のプログラミング言語(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;
そして、その「メッセージ」に応答するために、「イベントハンドラー」が応答します。単一のデリゲートか複数のデリゲートかは関係ありません。
要約:「イベント」は「質問」、「イベントハンドラー」は答えです。
これは私の解決策です:
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()){/*...*/}
パターンを呼び出すか使用する必要があります。
Delegate[] dary = TermCheckScore.GetInvocationList();
if ( dary != null )
{
foreach ( Delegate del in dary )
{
TermCheckScore -= ( Action ) del;
}
}
GetInvocationList
。
コールバックを手動で追加および削除し、どこでも多数のデリゲート型を宣言する代わりに:
// 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();
}
}