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

前端之家收集整理的这篇文章主要介绍了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.

猜你在找的C#相关文章