简体   繁体   中英

C# - How to map an object to a generic class suited to that object type?

(I really tried to come up with a better title, feel free to edit)

Suppose I have a generic event handler interface and implementations:

public interface IEventHandler<T>
{
    void HandleEvent(T t);
}

public class SlowButAccurateEventHandler<T> : IEventHandler<T>
{
    // To emphasize that the implementation depends on T
    private void SomeHelperClass<T> helperClass;

    public void HandleEvent(T t) { ... }
}

public class FastEventHandler<T> : IEventHandler<T>
{
    // To emphasize that the implementation depends on T
    private void SomeHelperClass<T> helperClass;

    public void HandleEvent(T t) { ... }
} 

and another class which I'd like to hold instances of EventHandlers, but cannot have generic methods since it's a WCF service:

public class MyService : MyContract
{
    // Pseudo (doesn't compile)
    private Dictionary<Type, IEventHandler<T>> eventHandlers;

    public MyService()
    {
        // More pseudo...
        eventHandlers = new Dictionary<Type, IEventHandler<T>>()
        {  
            { typeof(string), new SlowButAccurateEventHandler<string>() },
            { typeof(int), new FastEventHandler<int>() },
        };    
    }
    public void RouteToEventHandler(object userEvent)
    {
       var handler = eventHandlers[typeof(userEvent))];
       handler.HandleEvent(userEvent); // I realize that userEvent needs to be converted here
    }
}

So basically, I have some service ( MyService ) that I'd like to hold IEventHandlers and dispatch the correct handler when some event arrives. To do that, I'd like to keep a dictionary that holds a mapping between the CLR type and the suitable IEventHandler. Is that possible?

You should define your interface like this:

public interface IEventHandler<T>
{
    void HandleEvent(object t);
}

And then in the implementation:

public class FastEventHandler<T> : IEventHandler<T>
{
    // To emphasize that the implementation depends on T
    private void SomeHelperClass<T> helperClass;

    public void HandleEvent(object t)
    {
        if (t == null || !Type.Equals(t.GetType(), typeof(T)))
        {
            // We cannot handle the event.
            return;
        }

        T typedValue = Convert(t);

        // Here comes the rest of handling process.
    }

    private T Convert(object value)
    {
        try
        {
            return (T)value;
        }
        catch
        {
            return default(T);
        }
    }
} 

Another implementation, however I would stay at my previous answer:

public interface IEventHandler
{
    void HandleEvent(object value);
}

public interface IEventHandler<T> : IEventHandler
{
    void HandleEvent(T value);
}

public abstract class EventHandler<T> : IEventHandler<T>
{
    public void HandleEvent(object value)
    {
        if (value == null || !Type.Equals(value.GetType(), typeof(T)))
        {
            return;
        }

        HandleEvent(Convert(value));
    }

    private T Convert(object value)
    {
        try
        {
            return (T)value;
        }
        catch
        {
            return default(T);
        }
    }

    public abstract void HandleEvent(T value);
}

public class FastEventHandler<T> : EventHandler<T>
{
    public override void HandleEvent(T value)
    {
        throw new NotImplementedException();
    }
}

In the constructor you can initialize the event handlers:

var handlers = new Dictionary<Type, IEventHandler>()
{
    { typeof(string), new FastEventHandler<string>() },
    { typeof(int), new FastEventHandler<int>() }
};

Then:

public void RouteToEventHandler(object userEvent)
{
    if (userEvent == null)
    {
        return;
    }

    var handler = handlers[userEvent.GetType()];

    handler.HandleEvent(userEvent);
}

One way of course would be to store the handlers inside Dictionary<Type, object> and then use reflection to call the method in interest. In case you are interested in single method (like in your example), you can build and store a delegate call to that method instead, like this:

public class MyService : MyContract
{
    private Dictionary<Type, Action<object>> eventHandlers;

    static Action<object> GetHandler<T>(IEventHandler<T> handler)
    {
        var parameter = Expression.Parameter(typeof(object), "t");
        var body = Expression.Call(
            Expression.Constant(handler),
            "HandleEvent", null,
            Expression.Convert(parameter, typeof(T)));
        return Expression.Lambda<Action<object>>(body, parameter).Compile();
    }

    public MyService()
    {
        eventHandlers = new Dictionary<Type, Action<object>>()
        {
            { typeof(string), GetHandler(new SlowButAccurateEventHandler<string>()) },
            { typeof(int), GetHandler(new FastEventHandler<int>()) },
        };
    }

    public void RouteToEventHandler(object userEvent)
    {
        Action<object> handler;
        if (eventHandlers.TryGetValue(userEvent.GetType(), out handler))
            handler(userEvent);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM