web-dev-qa-db-ja.com

C#でイベントサブスクリプションをクリアするにはどうすればよいですか?

次のC#クラスを使用します。

_c1 {
 event EventHandler someEvent;
}
_

_c1_のsomeEventイベントへのサブスクリプションが多数あり、それらをすべてクリアしたい場合、これを達成する最良の方法は何ですか? このイベントへのサブスクリプションはラムダ/匿名デリゲートである可能性があることも考慮してください。

現在、私の解決策は、someEventをnullに設定するResetSubscriptions()メソッドを_c1_に追加することです。これが目に見えない結果をもたらすかどうかはわかりません。

135
programmer

クラス内から、(非表示の)変数をnullに設定できます。 null参照は、空の呼び出しリストを効果的に表す標準的な方法です。

クラスの外部からこれを行うことはできません。イベントは基本的に「サブスクライブ」と「サブスクライブ解除」を公開するだけです。

フィールドのようなイベントが実際に何をしているのかを知っておく価値があります-それらは変数を同時に作成しています。クラス内で、変数を参照することになります。外部から、イベントを参照します。

詳細については、私の イベントとデリゲートに関する記事 を参照してください。

174
Jon Skeet

「someEvent」をnullに設定するメソッドをc1に追加します...

class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = null;}
}
29
programmer
class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() {someEvent = delegate{};}
}

Nullよりデリゲート{}を使用する方が良い

7
Feng

すべてのサブスクライバーをクリアするベストプラクティスは、この機能を外部に公開する場合は、別のパブリックメソッドを追加してsomeEventをnullに設定することです。これは目に見えない結果をもたらしません。前提条件は、キーワード 'event'でSomeEventを宣言することを忘れないことです。

本をご覧ください-C#4.0の簡単な説明、125ページ。

ここの誰かが_Delegate.RemoveAll_メソッドを使用することを提案しました。使用する場合、サンプルコードは次の形式に従うことができます。しかし、それは本当に愚かです。なぜClearSubscribers()関数内に_SomeEvent=null_だけではないのですか?

_   public void ClearSubscribers ()
    {
          SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);// Then you will find SomeEvent is set to null.
    }
_
6
Cary

クラス内でイベントをnullに設定すると機能します。クラスを破棄するときは、常にイベントをnullに設定する必要があります。GCはイベントに問題があり、ダングリングイベントがある場合、破棄されたクラスをクリーンアップしない場合があります。

これを実現するには、Delegate.RemoveまたはDelegate.RemoveAllメソッドを使用します。

5
Micah

概念的な拡張退屈なコメント。

私はむしろ、「イベント」または「デリゲート」の代わりに「イベントハンドラ」という言葉を使用します。そして、「イベント」という言葉を他のものに使用しました。一部のプログラミング言語(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;

そして、その「メッセージ」に応答するために、「イベントハンドラー」は、単一のデリゲートか複数のデリゲートかを応答します。

要約:「イベント」は「質問」、「イベントハンドラー」は答えです。

3
umlcat

これは私の解決策です:

_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()){/*...*/}パターンを使用して、呼び出しリストのすべてのメンバーをサブスクライブ解除する必要があります。

1
Jalal

すべてのイベントを削除します。イベントが「アクション」タイプであると仮定します。

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}
0
Googol