ReSharper / C#で「デリゲート減算の結果は予測できません」?


124

myDelegate -= eventHandlerReSharper(バージョン6)の問題を使用する場合:

デリゲートの減算の結果は予測できません

この背後にある合理的な理由は、ここでJetBrainsによって説明されています。説明は理にかなっており、それを読んだ後、私は-デリゲートでの私の使用をすべて疑っています。

それでは

  • ReSharperを不機嫌にすることなく、自動以外のイベントを作成できますか?
  • または、これを実装するためのより良いおよび/または「正しい」方法はありますか?
  • または、ReSharperを無視できますか?

簡略化したコードは次のとおりです。

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

Monoでも同じ警告が表示されます。これは、問題に関するR#の説明ですconfluence.jetbrains.com/display/ReSharper/…(デリゲートのリストにのみ適用されます)
thoredge '19 / 10/19

回答:


134

恐れるな!ReSharperの警告の最初の部分は、デリゲートのリストの削除にのみ適用されます。コードでは、常に1つのデリゲートを削除しています。2番目の部分では、重複したデリゲートが削除された後のデリゲートの順序について説明します。イベントは、サブスクライバーの実行順序を保証するものではないため、実際には影響を受けません。

上記のメカニズムは予期しない結果をもたらす可能性があるため、ReSharperはデリゲート減算演算子に遭遇すると警告を発します。

ReSharperがこの警告を発行しているのは、マルチキャストデリゲートの減算には問題がある可能性があるためであり、その言語機能を完全に非難しているわけではありません。幸いなことに、これらの落とし穴はフリンジケースにあり、単純なイベントをインスツルメントしているだけの場合、それらに遭遇することはほとんどありません。独自のadd/ removeハンドラーを実装するためのより良い方法はありません、あなたはただ気づかなければなりません。

そのメッセージに対するReSharperの警告レベルを「ヒント」にダウングレードすることをお勧めします。そうすることで、通常有用な警告に鈍感にならないようにします。


66
結果を「予測不能」と呼ぶのはR#の悪いことだと思います。彼らは非常に明確に指定されています。「ユーザーが予測するものではない」と「予測できない」とはまったく同じではありません。(.NETフレームワークがオーバーロードを定義していると言うのも不正確です-C#コンパイラーに組み込まれてDelegateいます。オーバーロードせ+-。)
Jon Skeet

6
@ジョン:同意する。私は誰もがマイクロソフト自身が設定した高水準に慣れていると思います。.NETの世界には「成功の穴」に陥るほど多くのことがあり、それが存在する場所のピットのすぐ横を歩いているだけの言語機能に遭遇します。見落とす可能性があるため、耳障りであると考える人もいますPIT OF SUCCESS IS THAT WAY --->
Allon Guralnek

5
@AllonGuralnek:一方、これが原因で実際​​に問題が発生している人について最後に聞いたのはいつですか。
Jon Skeet

5
@ジョン:それで問題を聞いた?この質問が投稿されるまで、私はこの振る舞いについてさえ知りませんでした。
Allon Guralnek、2012年

2
R#がデリゲートの減算について警告するのは奇妙ですが、まったく同じ問題あるイベントの一般的な実装については警告しません。中心的な問題は、.netが単一のDelegate.Combineマルチキャストデリゲートを「フラット化」することです。そのため、デリゲート[X、Y]とZが指定されている場合、結果が[X、Y、Z]か[[ X、Y]、Z](後者のデリゲートは[X,Y]デリゲートをとして保持し、TargetそのデリゲートのInvokeメソッドをとして保持しますMethod)。
スーパーキャット2013

28

デリゲートを直接使用して合計または減算しないでください。代わりにあなたのフィールド

MyHandler _myEvent;

代わりにイベントとしても宣言する必要があります。これにより、ソリューションを危険にさらすことなく問題を解決し、イベントを使用できるという利点があります。

event MyHandler _myEvent;

デリゲートを単純に割り当てるとイベントを失う可能性があるため、デリゲートの合計または減算の使用は危険です(宣言に従って、開発者は、これがイベントとして宣言されているときのようにマルチキャストデリゲートであると直接推論しません)。例を挙げれば、この質問で言及されたプロパティがイベントとしてフラグが立てられていなかった場合、誰かがデリゲートに割り当てられただけであるため(以下も有効です!)、以下のコードは最初の2つの割り当てをLOSTにします。

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

Method3を割り当てるときに、2つの初期サブスクリプションを完全に失いました。イベントを使用すると、この問題が回避され、同時にReSharper警告が削除されます。


このようにすることは考えていませんでしたが、基になるイベントを非公開にしておく限り、イベントデリゲートの使用を保護することは理にかなっています。ただし、追跡が必要な複数のイベントサブスクリプションやサブサブスクリプションなど、追加/削除ハンドラーで発生する必要のある特定のスレッド同期がある場合、これはうまく機能しません。ただし、いずれにしても警告は削除されます。
ジェレミー

-17

-=を使用する代わりに、それを= nullに設定します


5
removeイベントのメソッドは、すべてのハンドラーを削除するのではなく、削除が要求されたハンドラーを削除する必要があります。
サービー2015

彼が1つだけ追加された場合、実際に1つだけ削除すると、すべて削除されます。私は、この特定のよりシャープなメッセージのためだけにすべてのケースでそれを使用すると言っているわけではありません。
ジョナサンベレスフォード

6
ただし、デリゲートが常に呼び出しリストの唯一の項目を削除していることはわかりません。これにより、正常に機能するコードが誤って機能しない誤ったコードに変換され、再シャープなメッセージが消えます。特定の状況では偶然に機能しますが、多くの場合、異常で診断が難しい方法で機能しなくなります。
サービー2015

-17は「潜在的な」回答を書くために時間を割く人にとってペナルティが厳しすぎるため、これを賛成しなければなりませんでした。たとえあなたが不正確だったとしても、受動的でないための+1。
ジョンC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.