简体   繁体   中英

C# listening to 3rd party long living event using weak reference

I am attaching to a 3rd party, long living Deleted event publisher, which ends up keeping my possibly short lived objects alive due to the event handler. The Deleted event is most likely never triggered, I just have to handle things if it is. It's not obvious where to un-subscribe from the Deleted event, thus I would like a weak reference to it so my objects can be GC'd.

I have seen a lot of very elaborate ways to create weak event handlers, but the following snippet seems to do the trick, at least in the provided test snippet. Is this just insane or can it work?

( http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx says under "A First Stab" that a similar snippet "(...) isn't robust enough to be used with an event (...)", why not?)

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;
    }
}

Test class

[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);
    }
}

Because Action<object, TArgs> handler maybe garbage collected before it's target is. Here's a unit test that exposes the problem:

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM