Garbage collection when using anonymous delegates for event handling

后端 未结 4 1773
灰色年华
灰色年华 2020-11-27 18:10

UPDATE

I have combined various answers from here into a \'definitive\' answer on a new question.

Original question

4条回答
  •  野性不改
    2020-11-27 18:38

    Building further on Egor's answer, I wanted to try and build a version where I didn't have to determine in advance which event I want to attach to.

    I've only managed to make it work with generic event handlers: for 'standard' event handlers (eg. FormClosingEventHandler), it's a bit tricky, because you can't have a type constraint where T : delegate (unless your name ends with Pony).

    private static void SetAnyGenericHandler(
         Action> add,     //to add event listener to publisher
         Action> remove,  //to remove event listener from publisher
         S subscriber,                    //ref to subscriber (to pass to consume)
         Action consume)            //called when event is raised*
             where T : EventArgs 
             where S : class
    {
        var subscriber_weak_ref = new WeakReference(subscriber);
        EventHandler handler = null;
        handler = delegate(object sender, T e)
        {
            var subscriber_strong_ref = subscriber_weak_ref.Target as S;
            if(subscriber_strong_ref != null)
            {
                Console.WriteLine("New event received by subscriber");
                consume(subscriber_strong_ref, e);
            }
            else
            {
                remove(handler);
                handler = null;
            }
        };
        add(handler);
    }
    

    (*I did try EventHandler consume here, but the calling code gets ugly because you have to cast s to Subscriber in the consume lambda.)

    Calling code example, taken from example above:

    SetAnyGenericHandler(
        h => publisher.EnabledChanged += h, 
        h => publisher.EnabledChanged -= h, 
        subscriber, 
        (Subscriber s, ValueEventArgs e) => s.Enabled = e.Value);
    

    Or, if you prefer

    SetAnyGenericHandler>(
        h => publisher.EnabledChanged += h, 
        h => publisher.EnabledChanged -= h, 
        subscriber, 
        (s, e) => s.Enabled = e.Value);
    

    It would be nice to be able to pass in the Event as just one parameter, but you can't access add/remove from an event any more than you can access get/set from a property (without doing yucky reflexion stuff, I think).

提交回复
热议问题