2017-04-04 4 views
2

標準イベントハンドラは、( - )=オペレータがunresgisteredない場合/(に配置)メモリリーク原因の一つです。
そして、マイクロソフトはWeakEventManagerとその継承を以下のように解決しました。PropertyChangedEventManager、CollectionChangedEventManager、CurrentChangedEventManager、ErrorsChangedEventManagerなどです。メモリリークと
すべての標準イベントハンドラをWeakEventManagerまたはそのvarianに置き換えても安全ですか? (オペレータ<strong>+ =</strong>有する)

簡単なコード例は、次のとおり

public class EventCaller 
{ 
    public static event EventHandler MyEvent; 

    public static void Call() 
    { 
     var handler = MyEvent; 
     if (handler != null) 
     { 
      handler(null, EventArgs.Empty); 
      Debug.WriteLine("============="); 
     } 
    } 
} 

public class A 
{ 
    string myText; 

    public A(string text) 
    { 
     myText = text; 
     EventCaller.MyEvent += OnCall; 

     // Use code below and comment out code above to avoid memory leakage. 
     // System.Windows.WeakEventManager<EventCaller, EventArgs>.AddHandler(null, "MyEvent", OnCall); 
    } 

    void OnCall(object sender, EventArgs e) 
    { 
     Debug.WriteLine(myText); 
    } 

    ~A() 
    { 
     Debug.WriteLine(myText + " destructor"); 
    } 
} 

void Main() 
{ 
    var a = new A("A"); 
    var b = new A("B"); 
    EventCaller.Call(); 
    a = null; 
    GC.Collect(); 
    EventCaller.Call(); 
} 

出力である:


B
+++++++

B
++++++++

私たちは、デストラクタが呼び出されないことを確認することができます。 しかし、我々はから(未使用のコードをコメント化)を変更する場合:

EventCaller.MyEvent += OnCall; 

System.Windows.WeakEventManager<EventCaller, EventArgs>.AddHandler(null, "MyEvent", OnCall); 

に、出力は次のとおりです。


B
++++ +++
B
+++++++
デストラクタ
Bのデストラクタ

ゼロにされた後、そのイベントハンドラはもう呼び出されません。
AとBは、 - =演算子なしで使用しないと削除されます。

1.すべての+ =演算子をSystem.Windows.WeakEventManagerに置き換えて、おそらくイベントの登録抹消やコードの保存が行えないためにIDisposableを実装しないでください。

2.本当に安全でない場合は、どうすればよいと思いますか?

答えて

2

すべての+ =演算子をSystem.Windows.WeakEventManagerに置き換えて、おそらくイベントの登録抹消やコードの保存が行えないためにIDisposableを実装しないでください。

できますか?多分。あなたはすべきですか?おそらくそうではありません。イベントハンドラへの強い参照がある場合は、強い参照を弱いイベントに置き換えるのではなく、イベントの発行者がイベントのサブスクライバより長く生きている場合は、そのイベントからの退会を好むべきです。弱いイベントを使用することの副作用があります。その一つがパフォーマンスです。もう一つは意味の違いです。あなたは、次の質問と.NET Frameworkのイベントの実装は、デフォルトで弱いイベントパターンを使用していない理由についての回答を参照することもできます。

Why is the implementation of events in C# not using a weak event pattern by default?

あなたが使用する必要がある特定のシナリオは確かにあります。弱いイベントパターンそのようなシナリオの1つは、ソースオブジェクトがリスナーオブジェクトから完全に独立しているWPFのデータバインディングです。しかし、これは弱いイベントパターンを常に使用すべきではありません。また、アプリケーションにサブスクリプションを廃棄するのをやめなければならないということも意味しません。

2

1)私は、IDisposableを実装するのではなく、WeakEventManagerを使用するために、引数として保存するコードを取ることはありません。ガベージcollecorリスナーを収集するまで

2)弱いイベントパターンの場合には、イベント処理は継続します。 廃棄パターンがない中で、強力な参照先イベントハンドラのすぐとして明示的に登録解除イベント処理を停止しないリスナーをUnreferencing。

3)は、リスナーがイベントに登録する必要があるとき

弱いイベントパターンWeak Event Patternsを使用することができますに関するMicrosoftのドキュメントを参照してくださいが、登録解除するとき、リスナーは、明示的に知りません。弱いイベントパターンは、ソースのオブジェクトの存続期間がリスナーの有効なオブジェクトの存続期間を超えるたびに使用することもできます。

リスナーを登録解除する時期を明示すれば、私は標準的なイベントを優先してDisposeパターンを実装したいと考えています。イベントハンドラの観点からは、明示的な登録解除には、ガベージコレクタがリスナを収集するまで、弱いイベントパターンがイベント(CPUおよびメモリ消費である可能性があります)を処理し続ける間に、イベント処理が直ちに停止するという利点があります。

+0

リスナーの参照を解除しても、すぐにイベント処理が停止することはないということに同意します。しかし、WeakEventManagerにはメソッドRemoveHandlerもあり、すぐにイベントハンドラが削除されると思います。したがって、イベントハンドラをいつ削除するかを制御できれば、それもできます。しかし、もし私たちが持っていなければ、それは後で自動的に行うでしょう。 –

+0

もちろん、弱いイベントパターンが、ソースオブジェクトをリスナーから完全に独立した状態に保つために推奨される解決策であるシナリオがあります。彼の答えで言及されているWPFデータバインディング 'mm8'は、一般的なパターンです。一方、弱いイベントマネージャが.NET Frameworkのデフォルトのイベントパターンではない理由は十分です。強い参照や弱い参照 - どちらも利点と副作用があります。それで、どのパターンを使うのかという特定の状況に応じて決めるのです。 – Patrick

関連する問題