C# · 12月 25, 2021

c# – 是否可以对泛型方法进行不安全的协变调用?

我有许多用于处理特定类型对象的类.

例如.,

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.