私はメモリ リークを避けるために WeakEventManager を使用してきましたが、使いすぎてしまいました。たとえば、次のような INotifyPropertyChanged の拡張メソッドを作成しました。
public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action handler)
{
PropertyChangedEventManager.AddHandler(item, (s, e) => handler(e.PropertyName), string.Empty);
}
今、私はこれがうまくいかないことにすぐに気づきました。実際、弱いイベント処理に匿名メソッドを実際に使用することはできません。(私が正しく理解していれば、コンパイラは(参照された値を保持するために)そのための「クロージャークラス」を作成します。これにはハンドラーがありますが、クロージャークラスはどこにも参照されていないため、GCはそれをクリアし、イベントハンドラー呼ばれません)
質問 #1: それは正しいですか? つまり、弱いイベント ハンドラーに匿名メソッド (またはラムダ) を使用する場合、その間に GC が実行されなかった場合にのみハンドラーが呼び出されます (たとえば、非決定論的です)。
まあ、そう思ったので、ユニットテストを行って、それが正しいことを確認しました。次の単体テストを実行するまでは問題ないようでした。
class DidRun
{
public bool Value { get; set; }
}
class TestEventPublisher
{
public event EventHandler<EventArgs> MyEvent;
public void RaiseMyEvent()
{
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
}
class TestClosure
{
public DidRun didRun { get; set; }
public EventHandler<EventArgs> Handler { get; private set; }
public TestClosure()
{
this.Handler = new EventHandler<EventArgs>((s, e) => didRun.Value = true);
}
}
[TestMethod]
public void TestWeakReference()
{
var raiser = new TestEventPublisher();
var didrun = new DidRun();
var closure = new TestClosure { didRun = didrun };
WeakEventManager<TestEventPublisher, EventArgs>.AddHandler(raiser, "MyEvent", closure.Handler);
closure = null;
GC.Collect();
GC.Collect();
raiser.RaiseMyEvent();
Assert.AreEqual(false, didrun.Value);
}
質問 #2: このテストが失敗する理由を説明できる人はいますか?
期待: ここでは、クロージャーはありません (何が起こっているかを確認するためにそれらを取り出しました)。WeakEventManager でイベントにサブスクライブするオブジェクト (クロージャー) があり、それへの参照をドロップします (閉鎖 = null;)。
古いクロージャー クラスをクリーンアップするために 2 つの GC.Collect() 呼び出しを期待していたので、WeakEventManager はサブスクライバーを削除し、ハンドラーを実行しませんが、テストは失敗します。何か案は?
編集:申し訳ありませんが、一般的な引数は表示されませんでしたが、現在は表示されています