標準イベントハンドラは、( - )=オペレータが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.本当に安全でない場合は、どうすればよいと思いますか?
リスナーの参照を解除しても、すぐにイベント処理が停止することはないということに同意します。しかし、WeakEventManagerにはメソッドRemoveHandlerもあり、すぐにイベントハンドラが削除されると思います。したがって、イベントハンドラをいつ削除するかを制御できれば、それもできます。しかし、もし私たちが持っていなければ、それは後で自動的に行うでしょう。 –
もちろん、弱いイベントパターンが、ソースオブジェクトをリスナーから完全に独立した状態に保つために推奨される解決策であるシナリオがあります。彼の答えで言及されているWPFデータバインディング 'mm8'は、一般的なパターンです。一方、弱いイベントマネージャが.NET Frameworkのデフォルトのイベントパターンではない理由は十分です。強い参照や弱い参照 - どちらも利点と副作用があります。それで、どのパターンを使うのかという特定の状況に応じて決めるのです。 – Patrick