13

次のような私のカスタムEventArgsを使用します。

public event EventHandler<MyEventArgs> SampleEvent;

msdnから 例:

public class HasEvent
{
// Declare an event of delegate type EventHandler of 
// MyEventArgs.

    public event EventHandler<MyEventArgs> SampleEvent;

    public void DemoEvent(string val)
    {
    // Copy to a temporary variable to be thread-safe.
        EventHandler<MyEventArgs> temp = SampleEvent;
        if (temp != null)
            temp(this, new MyEventArgs(val));
    }
}

2つの質問があります:

1)マークされたコードを見る:

ここに画像の説明を入力してください

別のパラメータにコピーする必要がある理由がわかりません(スレッドに関して)

keyowrdがあるeventので、誰もその呼び出しリストに触れることはできません(つまり、クラスの部外者コードはありません)

2)私が間違っていなければ、 DemoEvent関数は仮想である必要があります。これにより、サブクラスでオーバーライドできます...(どこかで見たことがあると思います)

奇妙なことに、resharperも仮想を追加しません:

したがって、このコードがある場合:

ここに画像の説明を入力してください

それは私を示唆しています:

ここに画像の説明を入力してください

そして私がそれを押すと:

ここに画像の説明を入力してください

もう一度私の2つの質問:

1) EventHandler<MyEventArgs> temp = SampleEvent;スレッドの安全性に関して、この行が解決するシナリオは何ですか?

2)関数はすべきではありませんvirtualか?(私はこのパターンを仮想で見たことがあると確信しています)

4

2 に答える 2

10

この行のシナリオは何ですかEventHandlertemp= SampleEvent; スレッドの安全性に関して、解決しますか?

あなたがこれを行うと想像してください:

if (SampleEvent != null)
    SampleEvent(this, new MyEventArgs());

ifの後、呼び出しの前に別​​のスレッドがイベントハンドラーをデタッチする場合は、nullデリゲートを呼び出そうとします(例外が発生します)。

関数は仮想であるべきではありませんか?(仮想でこのパターンを見たと確信しています)

はい、クラスがそうでない場合は、sealedその関数にマークを付ける必要がありますvirtual(必須ではありませんが、よく受け入れられているパターンです)。

編集

タイムスレッド1スレッド2
1 obj.SampleEvent + = MyHandler;
2 if(SampleEvent!= null)                     
3 {obj.SampleEvent-= MyHandler;
4 SampleEvent(this、new MyEventArgs());
5}

この場合、時間4でnullデリゲートを呼び出し、。をスローしNullReferenceExceptionます。次のコードを見てください。

タイムスレッド1スレッド2
1 obj.SampleEvent + = MyHandler;
2 var sampleEvent = SampleEvent;
3 if(sampleEvent!= null)                     
4 {obj.SampleEvent-= MyHandler;
5 sampleEvent(this、new MyEventArgs());
6}

ここで、時刻5に呼び出すと、の古いコンテンツがsampleEvent保持されます。この場合、例外はスローされません(ただし、2番目のスレッドによって削除された場合でも呼び出されます)。SampleEventMyHandler

于 2012-07-16T13:14:01.317 に答える
5
 if (SampleEvent != null)
    SampleEvent(this, new MyEventArgs(val));

これは古典的なスレッディングレースです。このコードの実行中に、別のスレッドがイベントハンドラーのサブスクライブを解除する可能性があります。これにより、if()ステートメントはサブスクライバーが存在すると結論付けますが、イベント呼び出しはNullReferenceExceptionで失敗します。デリゲートオブジェクトをローカル変数にコピーすることで、イベントハンドラーのサブスクライブを解除してデリゲートオブジェクト参照を変更するクライアントコードがクラッシュを引き起こさないようにします。それでも問題は、サブスクライブ解除後にイベントハンドラーを呼び出すことですが、これは避けられない競争であり、NREのように必ずしも致命的ではなく、NREとは異なりイベントハンドラーで処理できます。

はい、このようなメソッドは通常、保護された仮想になり、OnSampleEvent()という名前が付けられるため、派生クラスはイベント発生動作を変更できます。

于 2012-07-16T13:16:40.140 に答える