1

私は、イベント ハンドラーが原因で、存続期間が短い可能性のあるオブジェクトを存続させることになる、サード パーティの存続期間が長い Deleted イベント発行元にアタッチしています。Deleted イベントがトリガーされることはほとんどありません。発生した場合は処理する必要があります。Deleted イベントのサブスクライブを解除する場所が明確ではないため、オブジェクトを GC できるように弱い参照が必要です。

弱いイベント ハンドラーを作成する非常に精巧な方法を数多く見てきましたが、少なくとも提供されたテスト スニペットでは、次のスニペットがそのトリックを実行しているようです。これはただの狂気ですか、それともうまくいきますか?

( http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspxは、「A First Stab」の下で、同様のスニペットは「(...) で使用するほど堅牢ではありません。イベント(...)」、なぜですか?)

public static class WeakEvent
{
    private class WeakEventHolder<TArgs> where TArgs : EventArgs
    {
        private readonly WeakReference _handler;

        public WeakEventHolder(Action<object, TArgs> handler)
        {
            _handler = new WeakReference(handler);
        }

        public void Handle(object sender, TArgs args)
        {
            Action<object, TArgs> handler = (Action<object, TArgs>)_handler.Target;
            if (handler != null)
                handler(sender, args);
        }
    }

    public static EventHandler MakeHandler(Action<object, EventArgs> handler)
    {
        return new WeakEventHolder<EventArgs>(handler).Handle;
    }
}

テストクラス

[TestFixture]
public class Tests
{
    public class Publisher
    {
        public EventHandler Event;

        public void Raise()
        {
            if (Event != null)
                Event(this, EventArgs.Empty);
        }
    }

    public class Target
    {
        public Target(Publisher publisher)
        {
            publisher.Event += WeakEvent.MakeHandler(HandleEvent);
        }

        public void HandleEvent(object sender, EventArgs args)
        {
            System.Diagnostics.Trace.WriteLine("HandleEvent");
        }
    }

    [Test]
    public void Test()
    {
        Publisher publisher = new Publisher();
        WeakReference wref = new WeakReference(new Target(publisher));
        GC.Collect();

        publisher.Raise();

        Assert.False(wref.IsAlive);
    }
}
4

1 に答える 1

1

Action<object, TArgs> handlerガベージコレクションがターゲットになる前に収集される可能性があるためです。問題を明らかにする単体テストは次のとおりです。

public class Bar
{
    public void Foo(object sender, EventArgs args)
    {
    }
}

[Test]
public void ActionIsNotGCedBeforeTarget()
{
    Bar bar = new Bar();
    Action<object, EventArgs> action = bar.Foo;
    WeakReference weakRef = new WeakReference(action);
    action = null;
    GC.Collect();

    Assert.IsTrue(weakRef.IsAlive); // Will be false
}
于 2013-10-09T07:53:35.070 に答える