HodlDwon
HodlDwon

Reputation: 1171

Subscribe/Unsubscribe (add/remove) to event inside Extension Method

I want to create a fluent extension method for subscribing to (and less importantly unsubscribing from) an event. That is an extension with the usage .RespondBy(Method) in place of a += new Eventhandler(Method)

I want to do this: object.WhenSomethingChanges.RespondBy(DoingThisOtherThing);

Instead of this: object.WhenSomethingChanges += new EventHandler(DoingThisOtherThing);

I did a bunch of googling and while I didn't exactly grasp the intricate details, I do understand now that this has to do with whether you are accessing the local field or the public event.

With that said, I am simply interested in "how" this can be done and not concerned with "why" my first attempt hasn't worked. Failing a workaround, at least a definitive "You can not do this... at all, ever." would also be useful information...

CommuncationsStatusPresenter (Image)

Image of attempted usage, does not compile

CommuncationsStatusPresenter (Code)

using System;
using InspectionStation.Models;
using InspectionStation.Views;
using MachineControl.OPC;

namespace InspectionStation.Presenters
{
    public class CommuncationsStatusPresenter
    {
        // Fields
        private ICommunicationsModel m_model;
        private ICommunicationsView m_view;

        // Constructor
        public CommuncationsStatusPresenter
            (ICommunicationsModel p_model, ICommunicationsView p_view)
        {
            m_model = p_model;
            m_view = p_view;
            HookEvents();
        }
        private void HookEvents()
        {
            m_model
                .When_Communications_Pulses_Heartbeat
                .RespondBy(Setting_the_state_of_an_Indicator);
        }

        // Eventhandler
        void Setting_the_state_of_an_Indicator(Tag sender, EventArgs e)
        {
            bool State = sender.BooleanValue;
            m_view.Set_Communications_Status_Indicator = State;
        }
    }
}

RespondBy

using System;

namespace Common.Extensions
{
    public static partial class ExtensionMethods
    {
        public static
            void RespondBy<TSender, TEventArgs>(this
            GenericEventHandler<TSender, TEventArgs> p_event,
            GenericEventHandler<TSender, TEventArgs> p_handler
            ) where TEventArgs : EventArgs
        {
            p_event += new GenericEventHandler<TSender, TEventArgs>(p_handler);
        }
    }
}

GenericEventHandler

using System;

namespace Common
{
    [SerializableAttribute]
    public delegate void GenericEventHandler<TSender, TEventArgs>
        (TSender sender, TEventArgs e)
        where TEventArgs : EventArgs;
}

ICommunicationsModel

using System;
using Common;
using MachineControl.OPC;

namespace InspectionStation.Models
{
    public interface ICommunicationsModel
    {
        event GenericEventHandler<Tag, EventArgs>
            When_Communications_Pulses_Heartbeat;
    }
}

ICommunicationsView

namespace InspectionStation.Views
{
    public interface ICommunicationsView
    {
        bool Set_Communications_Status_Indicator { set; }
    }
}

Upvotes: 2

Views: 1379

Answers (2)

HodlDwon
HodlDwon

Reputation: 1171

Given Michael Perrenoud's answer, I have settled on doing the following as part of my pattern...

    public class CommuncationsStatusPresenter
    {
        ...

        private void HookEvents()
        {
            m_model.
                When_Communications_Pulses_Heartbeat += new EventHandler<Tag, EventArgs>(
                Set_the_state_of_an_Indicator);
        }

        // Eventhandler
        void Set_the_state_of_an_Indicator(Tag sender, EventArgs e)
        {
            ...
        }
    }
}

It's nothing special... just conistent auto-formatting based on new lines, but seems to be the best solution at this time (ie. I can almost read the code aloud to quickly decern its intent without resorting to extensive commented descriptions).

Note: I renamed the long GenericEventHandler to simply EventHandler as there's not much need for a special name.

Upvotes: 1

Mike Perrenoud
Mike Perrenoud

Reputation: 67898

Because the C# compiler strictly requires that events used outside the class must be followed by a += or -=, the attach and detach commands for handlers, it won't be possible to extend and use this outside of the class.

However, if you're willing to build more specific methods on the class itself like:

object.RespondWhenSomethingChangesBy(DoingThisOtherThing);

then you could, inside of that method, leverage the extension method because that class defined the event. I know it means you'd be building a lot of boiler plate code for the events, but if this is what you really want then the aforementioned might be found to be even more streamlined than:

object.WhenSomethingChanges.RespondBy(DoingThisOtherThing);

I do fully understand your position here, and hope that Microsoft chooses to allow events to be extended in the future (I can think of some interesting reasons I'd use it), but until then I guess we'll just have to work around it. Honestly, I'm confident there are some very good reasons it wasn't implemented in the first place, Microsoft does a pretty good job of thinking through these things.

Upvotes: 1

Related Questions