NimaAra / Easy.MessageHub

No need for .NET Events! A thread-safe, high performance & easy to use cross platform implementation of the Event Aggregator Pattern.
MIT License
258 stars 41 forks source link

Subscribing a runtime type #11

Closed odannyc closed 6 years ago

odannyc commented 6 years ago

I need to subscribe a runtime type which is defined with an attribute. For example: I have class Foo, which I want to subscribe to an Action (non-generic) That class Foo is defined by an attribute at runtime, meaning I can't do this:

Type t = typeof(Foo);
_messageHub.Subscribe<t>({Some method})

I know this isn't valid syntax so this won't work. Is there anyway to make this work? I was thinking something like this, but not sure if it'll work:

Type t = typeof(Foo);
_messageHub.Subscribe(t, {Some method})
NimaAra commented 6 years ago

I intend to keep the interface generic to avoid boxing but you can use the GlobalHandler in your specific case:

hub.RegisterGlobalHandler((type, eventObject) => Console.WriteLine($"Type: {type} - Event: {eventObject}"));
odannyc commented 6 years ago

This won't work because the event object is not known until the Publish phase.

I looked through the code and saw some ways to do it. Can we do this:

Make the Register method:

        internal static Guid Register(TimeSpan throttleBy, Action<object> action, Type type)
        {
            var key = Guid.NewGuid();
            var subscription = new Subscription(type, key, throttleBy, action);

            lock (AllSubscriptions)
            {
                AllSubscriptions.Add(subscription);
                _subscriptionsChangeCounter++;
            }

            return key;
        }

These would now be the Subscribe overloads:

public Guid Subscribe<T>(Action<T> action) => Subscribe(action, TimeSpan.Zero, typeof(T));

public Guid Subscribe<T>(Action<T> action, TimeSpan throttleBy) => Subscribe(action, throttleBy, typeof(T));

public Guid Subscribe(Action<object> action, TimeSpan throttleBy, Type type)
{
        EnsureNotNull(action);
        return Subscriptions.Register(throttleBy, action, type);
}
NimaAra commented 6 years ago

It does work, RegisterGlobalHandler receives every message published. You will have the type as well as the payload.

odannyc commented 6 years ago

Okay, just tested and it works just for one Type.. I have multiple Types ... So this won't work with my use case. This is my test code:

                _hub.RegisterGlobalHandler((type, eventObject) =>
                {
                    if (type == externalType)
                    {
                        listener.Handle(eventObject);
                    }
                });

Each time RegisterGlobalHandler is called, it overrides the last one.

Any other way to accomplish this? What do you think of my suggestion above?

NimaAra commented 6 years ago

It does work, I am not going to change the interface (add or remove).

The purpose of RegisterGlobalHandler is to register 1 single handler which will receive every message of every type passed through the hub. It is not meant to build up multiple handlers.

In your case you can compose a handler which knows how to behave based on the types that you are interested in at run-time then register that as the global handler.

 _hub.RegisterGlobalHandler((type, eventObject) =>
{
    if (type == firstType)
    {
        // handle eventObj        
    } 
    else if (type == secondType)
    {
        // handle eventObj
    }
    ...
});