更新
SomeEvent?.Invoke(this, e);
私は頻繁に次のアドバイスを聞いたり読んだりします:
確認して実行する前に、必ずイベントのコピーを作成してくださいnull
。これにより、null
nullを確認する場所とイベントを発生させる場所の間にイベントが発生する、スレッドに関する潜在的な問題が解消されます。
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
更新:最適化について読んだことから、これにはイベントメンバーも揮発性である必要があるかもしれないと思いましたが、ジョンスキートは彼の回答でCLRはコピーを最適化しないと述べています。
しかし、その間、この問題が発生するためには、別のスレッドが次のようなことをしている必要があります。
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
実際のシーケンスは次のようになります。
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
要点はOnTheEvent
、作成者が購読を解除した後でありますが、それが発生しないように、特に購読を解除しただけです。確かに本当に必要なのは、add
とremove
アクセサで適切に同期するカスタムイベントの実装です。さらに、イベントが発生したときにロックが保持されると、デッドロックが発生する可能性があるという問題があります。
それでは、このカーゴカルトプログラミングですか?それはそのようです-マルチスレッド設計の一部として使用できるようになる前に、実際にはイベントはこれよりもはるかに注意を必要とするように思えますが、多くの人々はコードを複数のスレッドから保護するためにこのステップを踏まなければなりません。その結果、その追加の注意を払っていない人々はこのアドバイスを無視するかもしれません-それは単にシングルスレッドプログラムの問題ではありません、そして実際、volatile
ほとんどのオンラインサンプルコードに欠けているとすれば、アドバイスはないかもしれませんまったく効果。
(そして、最初delegate { }
にチェックする必要がないように、メンバー宣言に空を割り当てるだけの方がずっと簡単ではないnull
ですか?)
更新しました:不明確な場合でも、私はアドバイスの意図を理解しました。あらゆる状況でnull参照例外を回避するためです。私のポイントは、この特定のnull参照例外は、別のスレッドがイベントから登録解除されている場合にのみ発生する可能性があることであり、これを行う唯一の理由は、そのイベントを介してそれ以上の呼び出しが受信されないようにすることです。これは、この手法では明らかに達成されません。 。あなたは競合状態を隠しているでしょう-それを明らかにする方が良いでしょう!このnull例外は、コンポーネントの不正使用を検出するのに役立ちます。コンポーネントを不正使用から保護したい場合は、WPFの例に従う-コンストラクターにスレッドIDを格納し、別のスレッドがコンポーネントと直接対話しようとした場合に例外をスローします。または、本当にスレッドセーフなコンポーネントを実装します(簡単な作業ではありません)。
したがって、このコピー/チェックのイディオムを実行するだけでは、カーゴカルトプログラミングであり、コードに混乱やノイズが加わると思います。他のスレッドから実際に保護するには、さらに多くの作業が必要です。
Eric Lippertのブログ投稿に応じて更新:
したがって、イベントハンドラーについて見逃していた重要なことがあります。「イベントハンドラーは、イベントがサブスクライブされた後でも呼び出される前に堅牢である必要がある」ため、イベントの可能性のみを考慮する必要があります。委任null
。イベントハンドラーの要件はどこかに文書化されていますか?
そして、「この問題を解決する方法は他にもあります。たとえば、ハンドラーを初期化して、削除されない空のアクションを設定します。ただし、nullチェックを行うのが標準的なパターンです。」
だから私の質問の残りの断片は、なぜ「標準パターン」を明示的にヌルチェックするのですか?代わりに、空のデリゲートを割り当てる= delegate {}
と、イベント宣言に追加するだけで済みます。これにより、イベントが発生するすべての場所から、これらの小さな臭い儀式の山がなくなります。空のデリゲートをインスタンス化するのが安価であることを確認するのは簡単です。または、まだ何か不足していますか?
確かにそれは(Jon Skeetが示唆したように)これは2005年に行われるはずだった、消えていない.NET 1.xのアドバイスにすぎないのでしょうか?
EventName(arguments)
がイベントのデリゲートを無条件に呼び出すのではなく、無条件にデリゲートを呼び出すことを決定したことです(nullの場合は何もしない)。