我有许多用于处理特定类型对象的类.
例如.,
class FooHandler : Handler<Foo> { void ProcessMessage(Foo foo); }
处理程序接口可能定义如下:
interface Handler<T> { void ProcessMessage(T obj); }
现在,我希望能够使用这些处理程序的字典:
Dictionary<Type,Handler> handlers; void ProcessMessage(object message) { var handler = handlers[message.GetType()]; handler.ProcessMessage(handler); }
但是,C#似乎不允许我在不指定类型的情况下使用Handler接口. C#也不允许我声明接口Handler< out T>所以我不能使用Handler< object>在处理程序声明中.
即便这样也行不通:
Dictionary<Type,object> handlers; void ProcessMessage(object message) { dynamic handler = handlers[message.GetType()]; handler.ProcessMessage(message); }
这似乎可以使用反射解决:
handler.GetType().GetMethod("ProcessMessage").Invoke(handler,new object[] { message });
当然,我可以从Handler界面中删除泛型.但是,我走这条道路的原因是我想让处理程序的API尽可能简单.我希望类指定它们收到的消息,并让它们能够处理这些消息而不必在每个方法中转换参数.
如果可能的话,我宁愿避免反思,完全避免仿制药似乎并不令人满意.
我错过了一些明显的东西,还是我在克服C#的泛型限制?
我意识到C#不是Java(使用Java的类型擦除,这很容易),也许这可能更好地以类似C#的方式解决…所以我也对其他方法感兴趣.
谢谢!
解决方法
有一个更好的办法.只需将演员嵌入lambda并存储动作而不是处理程序:
Dictionary<Type,Action<object>> handlers; void AddHandler<T>( Handler<T> handler ) { handlers.Add(typeof(T),m => handler.ProcessMessage((T)m)); } void ProcessMessage(object message) { var handler = handlers[message.GetType()]; handler(message); }
好的,这超出了问题的范围,但评论中的讨论引导我们:
interface IMessage {} class Foo : IMessage {} interface Handler<T> where T : IMessage { void ProcessMessage(T obj); } class FooHandler : Handler<Foo> { public void ProcessMessage(Foo foo) {} } class Program { static readonly Dictionary<Type,Action<object>> handlers = new Dictionary<Type,Action<object>>(); static void AddHandler<T>(Handler<T> handler) where T : IMessage { handlers.Add(typeof(T),m => handler.ProcessMessage((T)m)); } static void ProcessMessage(object message) { var handler = handlers[message.GetType()]; handler(message); } public static IEnumerable<Type> GetAllTypes() { return AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()); } public static IEnumerable<Type> GetDerivedFrom<T>() { return GetAllTypes().Where(t => IsDerivedFrom(t,typeof(T))); } static bool IsDerivedFrom(Type t,Type parent) { return parent.IsAssignableFrom(t) && t!=parent; } static void Main() { var handlerTypes = from handlerBaseType in GetDerivedFrom<IMessage>().Select(t => typeof(Handler<>).MakeGenericType(t)) select GetAllTypes().FirstOrDefault(t => IsDerivedFrom(t,handlerBaseType)) into handlerType where handlerType!=null select Activator.CreateInstance(handlerType); foreach (object handler in handlerTypes) { AddHandler((dynamic)handler); Console.WriteLine("Registered {0}.",handler.GetType()); } } }
不涉及任何字符串…当然,如果您想通过命名来建立约定,则可以使扫描更容易,只需在注释中从消息类型的名称中查找处理程序类型.您还可以使用IOC容器替换Activator.CreateInstance.