通用消息处理程序

kub*_*003 8 c# generics

我想创建一个通用机制来处理C#中的消息.我在我的小应用程序中需要这个,所以我不想使用完整的消息总线.我的要求很简单:

  • 我想有几个类的消息,即Message1,Message2.他们可以从一个基类继承,这不是问题,但如果不是,我不在乎.目前他们确实继承了Message.
  • 能够为每个消息类获取处理程序.即如果我发送Message1,那么Message1Handler应该实例化类.处理程序必须实现IMessageHandler<T>,其中T是消息类.IMessageHandler定义如下:

    interface IMessageHandler<T>
    {
        void Execute(T message);
    }
    
    Run Code Online (Sandbox Code Playgroud)

我写了一个简单的"解析器"类:

public static class HandlerRegistry
{
    private static readonly Dictionary<string, Type> _handlers = new Dictionary<string, Type>(); 


    public static void Register<T, T2>() where T2: IMessageHandler<T>
    {
        _handlers.Add(typeof(T).FullName, typeof(T2));

    }

    public static IMessageHandler<T> Resolve<T>(T parameters)
    {
        var type = _handlers[parameters.GetType().FullName];
        return (IMessageHandler<T>) Activator.CreateInstance(type);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个实现中,一切都很好,但是一部分 - 转换为IMessageHandler.当我试图将它与消息集合一起使用时,会发生这样的情况:编译器在编译时不知道集合中将会有什么实际消息 - 它只是假设它们都是子类Message,所以它是试图投射IMessageHandler<ConcreteMessage>IMessageHandler<Message>显然我得到一个无效演员的异常.在这种情况下,可能的逆变会有所帮助,但我无法将参数声明为out,因为我在Execute方法参数中有消息.

有谁知道这个问题的优雅解决方案?我知道我可以使它"更加运行时" - 而不是使用泛型只是声明 void Execute(Message m)并且在每个处理程序中开始尝试强制转换为我期望的类型,但是有人说某处 - 你写的每一个演员破坏了使用类型系统的全部意义.

AK_*_*AK_ 2

对于消息路由器来说怎么样:

 class Tester
    {
        public void Go()
        {
            var a = new MessageA();
            var b = new MessageB();
            var c = new MessageC();

            var router = new MessageRouter();
            router.RegisterHandler(new HandlerA());
            router.RegisterHandler(new HandlerB());

            router.Route(a);
            router.Route(b);
            router.Route(c);
        }
    }

    class MessageRouter
    {
       Dictionary<Type, dynamic> m_handlers = new Dictionary<Type,dynamic>();

        public void RegisterHandler<T>(IMessageHandler<T> handler)
        {
            m_handlers.Add(typeof(T), handler);
        }

        public void Route(dynamic message)
        {
            var messageType = message.GetType();
            if (m_handlers.ContainsKey(messageType))
            {
                m_handlers[messageType].Handle(message);
            }
            else
            {
                foreach (var pair in m_handlers)
                {
                    if(pair.Key.IsAssignableFrom(messageType))
                    {
                        pair.Value.Handle(message);
                    }
                }
            }
        }

    }

    class MessageA
    {
        public virtual string A { get { return "A"; } }
    }
    class MessageB
    {
        public  string B { get { return "B"; } }
    }

    class MessageC :MessageA
    {
        public  override string A {  get { return "C"; } }
    }
    interface IMessageHandler<T>
    {
        void Handle(T message);
    }
    class HandlerA : IMessageHandler<MessageA>
    {

        public void Handle(MessageA message)
        {
            Console.WriteLine(message.A);
        }
    }
    class HandlerB : IMessageHandler<MessageB>
    {

        public void Handle(MessageB message)
        {
            Console.WriteLine(message.B);
        }
    }
Run Code Online (Sandbox Code Playgroud)