イベントハンドラーは既に追加されていますか?


183

イベントハンドラーがオブジェクトに追加されたかどうかを確認する方法はありますか?SQLベースのセッションステートを使用できるように、オブジェクトのリストをセッションステートに出入りするようにシリアル化しています...リスト内のオブジェクトのプロパティが変更された場合は、フラグを立てる必要があります。 。ただし、オブジェクトが逆シリアル化されると、イベントハンドラーが取得されなくなります。

ちょっとした煩わしさで、オブジェクトにアクセスするGetプロパティにイベントハンドラーを追加しました。それは今呼び出されていますが、5回のように呼び出されることを除いて素晴らしいので、オブジェクトがアクセスされるたびにハンドラーが追加され続けるだけだと思います。

無視するだけで十分安全ですが、ハンドラーが既に追加されているかどうかを確認して、1回だけ追加することで、よりクリーンにしたいと思います。

それは可能ですか?

編集:私は必ずしもどのイベントハンドラーを追加するかを完全に制御できるわけではないので、nullをチェックするだけでは十分ではありません。


回答:


123

@Telosが言及するように、定義クラスの外側からは、a +=またはの左側でのみEventHandlerを使用できます-=。したがって、定義クラスを変更する機能がある場合は、イベントハンドラが存在するかどうかを確認することにより、チェックを実行するメソッドを提供できます。そうである場合、イベントハンドラはnull追加されていません。そうでない場合は、多分、Delegate.GetInvocationListの値をループ処理できます 。1つがイベントハンドラーとして追加するデリゲートと等しい場合、そこにあることがわかります。

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

そして、これは「ハンドラーがない場合はハンドラーを追加する」になるように簡単に変更できます。イベントを公開しているクラスの内部にアクセスできない場合は、@ Lou Francoの提案に従って、-=とを探索する必要があります+=

ただし、これらのオブジェクトをコミッションおよびデコミッションする方法を再検討して、この情報を自分で追跡する方法が見つからないかどうかを確認した方がよい場合があります。


7
これはコンパイルされません。EventHandlerは+ =または-=の左側にのみ配置できます。
CodeRedick 2008

2
詳細な説明に基づいて反対票を削除しました。SQLの状態は、ここのアイデア全体をかなり破壊しています... :(
CodeRedick

1
ブレアとSOの検索に感謝します。私が探していたものだけです(クラスの外ではできないので困ります)
George Mauer

3
デリゲートを比較するときに問題が発生するのは、ほとんどの場合です。したがってDelegate.Equals(objA, objB)、まったく同じデリゲートの存在を確認する場合に使用します。それ以外の場合は、のようにプロパティを個別に比較しますif(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName)
Sanjay

2
このコードはWinFormでは機能しません。それは厳密にASP.NET用ですか?
jp2code

213

私は最近、イベントのハンドラーを1回だけ登録する必要がある、同様の状況になりました。ハンドラーがまったく登録されていなくても、最初に安全に登録を解除してから、再度登録できることがわかりました。

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

ハンドラーを登録するたびにこれを行うと、ハンドラーが1回だけ登録されることが保証されます。私にはかなり良い習慣のように聞こえます:)


9
危険そうです。ハンドラーを削除した後、元に戻す前にイベントが発生した場合、そのイベントは失われます。
ジミー

27
承知しました。つまり、スレッドセーフではありません。しかし、それは通常ではない、複数のスレッドなどを実行しているときにのみ問題になる可能性があります。ほとんどの場合、これは単純化のために十分なはずです。
ALF

7
これは私を悩ませます。現在、コードで明示的にスレッドを作成していないからといって、複数のスレッドがないことや、後で追加されることはありません。あなた(またはチーム内の他の誰か、場合によっては数か月後に)にワーカースレッドを追加するか、UIとネットワーク接続の両方に応答するとすぐに、非常に断続的なドロップイベントへの扉が開きます。
テクノフィル2015

1
削除してから再度追加するまでの時間枠は非常に短いため、見逃したイベントが存在することはほとんどありませんが、それでも可能です。
Alisson 2017

2
UIの更新などにこれを使用している場合、そのような些細なタスクのリスクは大丈夫です。これがネットワークパケット処理などの場合は、使用しません。
2017

18

これが唯一のハンドラーである場合は、イベントがnullかどうかを確認できます。それ以外の場合は、ハンドラーが追加されています。

追加されていない場合でも、ハンドラーを使用してイベントで-=を安全に呼び出すことができると思います(追加されていない場合はキャッチできます)。追加する前に、そこにないことを確認してください。


3
このロジックは、イベントが他の場所でも処理されるとすぐに壊れます。
bugged87

6

この例は、メソッドGetInvocationList()を使用して、追加されたすべてのハンドラーへのデリゲートを取得する方法を示しています。特定のハンドラー(関数)が追加されているかどうかを確認する場合は、配列を使用できます。

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

デリゲートのMethodプロパティのさまざまなプロパティを調べて、特定の関数が追加されているかどうかを確認できます。

アタッチされているものが1つだけかどうかを確認する場合は、nullかどうかをテストできます。


GetInvocationList()は私のクラスのメンバーではありません。実際、私がアクセスできるオブジェクトやハンドラーでこのメソッドを見つけることができないようです...
CodeRedick

私も試してみましたが、クラス内からしかそのようなイベントにアクセスできません。私はこれを一般的に行っていますが、他の人が述べたように、イベントハンドラはおそらくとにかく失われています。説明をありがとう!
CodeRedick 2008

4

私があなたの問題を正しく理解していれば、もっと大きな問題があるかもしれません。他のオブジェクトがこれらのイベントをサブスクライブする可能性があると述べました。オブジェクトがシリアル化および逆シリアル化されると、他のオブジェクト(ユーザーが制御できないオブジェクト)はイベントハンドラーを失います。

それについて心配していない場合は、イベントハンドラへの参照を保持することで十分です。イベントハンドラーを失う他のオブジェクトの副作用について心配している場合は、キャッシュ戦略を再考することをお勧めします。


1
ああ!それさえ考えていませんでした...私の元の問題を考えると明らかだったはずですが、私自身のハンドラーが失われました。
CodeRedick 2008

2

私はアルフの答えに同意しますが、それを少し変更することは、使用することです、

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }

2

私のために働いた唯一の方法は、イベントを追加するときにブール変数をtrueに設定することです。次に質問します。変数がfalseの場合、イベントを追加します。

bool alreadyAdded = false;

この変数はグローバルにすることができます。

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}

0
EventHandler.GetInvocationList().Length > 0

2
リスト== nullの場合、これはスローされませんか?
Boris Callens、

2
イベントハンドラーを所有するクラスの外では、-=と+ =しか使用できません。イベントにアクセスできません。
tbergelt 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.