1

Consider the following code:

public interface IEventHandler<in T> where T : IEvent
{
    void Execute(T @event);
}

For these handlers I implement some concrete handler-subclasses:

public class SomeEventHandler : IEventHandler<SomeEvent>
{
    public void Execute(SomeEvent @event) { /* ... */ }
}

Now I have a factory to retrieve the corresponding handler of an event:

public class EventHandlerFactory : IEventHandlerFactory
{
    public IEventHandler<T> Create<T>(T @event) where T : IEvent
    {
        // What to do in here?
    }
}

I tried to check the type of the event and then return the corresponding handler, but of course the type-system denies that:

if(@event is SomeEvent)
{
    return (IEventHandler<T>) new SomeEventHandler();
}

I wouldn't do this so hacky, but I wonder how to allow the code to make that interface with the Create<T> possible.

EDIT: When I loop through an enumarable of events the T is treated like IEvent so the type-system will throw an exception:

var events = IEnumarable<IEvent>() { /* ... */ };

foreach (var @event in events)
{
    var eventHandler = eventHandlerFactory.Create(@event);
}

The Create(T @event)-method will throw an exception because T is an IEvent and not the concrete type. I could solve it using (dynamic) @event but is not what I really want to do.

Luca Nate Mahler
  • 1,292
  • 2
  • 13
  • 28
  • [Please don't put tags in question titles](https://stackoverflow.com/help/tagging) – Liam May 23 '17 at 14:00
  • Possible duplicate of [Create instance of generic type?](https://stackoverflow.com/questions/731452/create-instance-of-generic-type) – Liam May 23 '17 at 14:02
  • What creates this - `events = IEnumarable()` - what's in the commented out code? Where do the `IEvent`s come from – Alex May 24 '17 at 14:33

2 Answers2

1

Classic factory pattern.
What I like to do for things like this is the following:

public class EventHandlerFactory : IEventHandlerFactory
{
    private readonly Dictionary<Type, Type> _eventHandlers = new Dictionary<Type, Type>();

    public EventHandlerFactory()
    {
        //add a mapping between the type, and the handler
        //note - this could be done with reflection to automate this
        _eventHandlers.Add(typeof(SomeEvent), typeof(SomeEventHandler));
    }

    public IEventHandler<T> Create<T>(T @event) where T : IEvent
    {
        var handler = _eventHandlers[typeof(T)];

        if (handler != null)
        {
            //now use Activator.CreateInstance to instantiate the type
            return (IEventHandler<T>)Activator.CreateInstance(handler);
        }

        throw new Exception("Handler not found");
    }
}
Alex
  • 37,502
  • 51
  • 204
  • 332
  • This is how I would do it, though perhaps with a few minor tweaks. For example, it may be better as a static class. Also consider throwing an exception rather than returning null. – DavidG May 23 '17 at 14:15
  • Didn't make it explicitly static as I normally handle this via an ioc container - I make my instantiation of `IEventHandlerFactory` a single instance – Alex May 23 '17 at 14:17
  • 1
    Yeah, that's a great reason to keep it non-static. – DavidG May 23 '17 at 14:20
  • @Alex Thanks for your answer. Unfortunately I face another (maybe related) problem with this. I loop through an IEnumerable to get the corresponding handlers from the factory; and because I do this the T is always IEvent and not the concrete type SomeEvent so an InvalidCastException will be thrown because these types don't match. – Luca Nate Mahler May 24 '17 at 06:14
  • Not sure I understand.. Can you edit your question to show that code please? – Alex May 24 '17 at 06:33
0

Alternative solution:

If you want to avoid reflection or your IEventHandler implementations do not have default constructors:

public interface IEventHandler
{
}

public interface IEventHandler<in T> : IEventHandler where T : IEvent
{
    void Execute(T @event);
}

public class EventHandlerFactory
{
    private static readonly Dictionary<Type, Func<IEventHandler>> _eventHandlers = new Dictionary<Type, Func<IEventHandler>>
    {
        { typeof(SomeEvent), () => new SomeEventHandler() }
    };

    public IEventHandler<T> Create<T>(T @event) where T : IEvent
    {
        Func<IEventHandler> handler;
        if (_eventHandlers.TryGetValue(typeof(T), out handler))
        {
            return (IEventHandler<T>)handler();
        }

        throw new Exception("Handler not found");
    }
}
Yevgeniy
  • 1,054
  • 1
  • 9
  • 17