elios264
elios264

Reputation: 404

WeakReference too weak?

I've implemented this WeakEvent Delegate but for some reason it only works for a few seconds, and after that it stops working:

public class WeakEventHandler<TE>  where TE : EventArgs
{
    private readonly Action<WeakEventHandler<TE>> _unsubscriber;
    private readonly WeakReference<EventHandler<TE>> _targetRef;
    private readonly EventHandler<TE> _handler;


    public WeakEventHandler(EventHandler<TE> eventReceiver, Action<WeakEventHandler<TE>> unsubscriber)
    {
        _unsubscriber = unsubscriber;
        _targetRef = new WeakReference<EventHandler<TE>>(eventReceiver);
        _handler = Invoke;
    }

    public void Invoke(object sender, TE e)
    {
        EventHandler<TE> method;

        if (_targetRef.TryGetTarget(out method))
        {
            method(sender, e);
        }
        else
        {
            _unsubscriber(this);
        }

    }

    public static implicit operator EventHandler<TE>(WeakEventHandler<TE> weh)
    {
        return weh._handler;
    }
}

public static class EventHandlerUtils
{
    public static EventHandler<TE> MakeWeak<TE>(this EventHandler<TE> eventReceiver, Action<WeakEventHandler<TE>> unsubscriber) where TE : EventArgs
    {
        return new WeakEventHandler<TE>(eventReceiver, unsubscriber);
    }
}

Usage:

    private EventHandler<MouseInteractionArgs> _mouseInteractionDelegate;

    public event EventHandler<MouseInteractionArgs> MouseAction
    {
        add
        {
            _mouseInteractionDelegate += value.MakeWeak(handler => _mouseInteractionDelegate -= handler );
        }

        remove
        {
            throw new InvalidOperationException("This is a weak Event, dont worry about unsubscribing");
        }
    }

I never stop referencing the owner of the method, so I dont know why it is not working, do delegates behave different?

Upvotes: 2

Views: 281

Answers (2)

elios264
elios264

Reputation: 404

Based on your answer I was able to fix the Weakeventhandler:

public class WeakEventHandler<TE> where TE : EventArgs
{
    private delegate void OpenEventHandler(object target, object sender, TE e);

    private readonly WeakReference _targetRef;
    private readonly OpenEventHandler _openHandler;

    private readonly Action<WeakEventHandler<TE>> _unsubscriber;
    private readonly EventHandler<TE> _handler;


    public WeakEventHandler(EventHandler<TE> subscriber, Action<WeakEventHandler<TE>> unsubscriber)
    {
        _unsubscriber = unsubscriber;
        _targetRef = new WeakReference(subscriber.Target);
        _handler = Invoke;


        var target = Expression.Parameter(typeof (object), "target");
        var sender = Expression.Parameter(typeof(object), "sender");
        var args = Expression.Parameter(typeof (TE), "args");

        _openHandler =
            Expression.Lambda<OpenEventHandler>(
                Expression.Call(Expression.Convert(target, subscriber.Target.GetType()), subscriber.Method, sender,
                    args),target,sender,args).Compile();
    }

    public void Invoke(object sender, TE e)
    {
        var t = _targetRef.Target;

        if (t != null)
        {
            _openHandler(t, sender, e);
        }
        else
        {
            _unsubscriber(this);
        }

    }

    public static implicit operator EventHandler<TE>(WeakEventHandler<TE> weh)
    {
        return weh._handler;
    }
}

I'm building a component-based system, where components of an Object sometimes need to interact with for example mouse events, and I dont want to unregister each component for each object I remove from the world.

Upvotes: 1

Peter Duniho
Peter Duniho

Reputation: 70661

but the "eventreceiver" is owned by the instance of the method

Sorry, but you have that backwards. The "eventReceiver" is the delegate instance that you are adding to the MouseAction event. That delegate instance retains a reference to the target object and MethodInfo, not the other way around. There's no code retaining the reference to the delegate instance except your WeakReference and of course by design that's not enough to keep the object alive.

Upvotes: 3

Related Questions