编程语言
首页 > 编程语言> > 如何将消息反序列化为强类型对象,然后为该消息动态调用运行时分配的处理程序

如何将消息反序列化为强类型对象,然后为该消息动态调用运行时分配的处理程序

作者:互联网

ew,真是个头衔…

我正在为一个学习项目的服务器上工作.我花了很多时间试图弄清楚如何正确地解决这个问题.一开始,我不认为我什至完全知道自己想要达到的目标.

我正在使用的是具有N个组件(N> = 0)的服务器.

>在运行时使用DI添加每个组件.
>每个组件都是一个“黑匣子”.当它收到一条消息时,它被认为是“工作单元”,并且具有从头到尾所需的一切.
>每个组件负责提供用于订阅消息的信息,并提供该消息的处理程序.我计划使用处理程序函数上的属性来实现此目的.

“处理程序”的示例:

[Handles(ExampleMessage)]
private void handleExampleMessage(ExampleMessage message)
{
    DoStuff();
}

这是构想我的问题的最清晰方法:

如何实现类型化的“消息代理”系统,例如ASP.NET MVC如何从序列化输入为控制器操作提供类型化的模型.

所以我想实现的是:

序列化消息->强类型消息->消息服务-> *强类型*消息作为参数的调用处理程序函数

我想到了几件事:

我尝试做的第一件事就是简单地将消息反序列化为动态消息,但是对于智能化而言,没有智能和编译时检查的成本太高了.

然后,我尝试在运行时使用反射创建静态反序列化方法,并使用这些方法的返回值调用“处理程序”,但是它是如此丑陋和细致,我只好删除了它(尽管我当然仍然开放如果有人有优雅,注重性能的方式,请选择此选项)

最终,我尝试使用所有消息都继承自的消息类型,但是当我尝试使用Dictionary< Action< Message>,Message>调用适当的处理程序

解决方法:

这是可能的,并且只有一点点复杂.您要做的是在组件中搜索具有Handles属性的方法,然后通过反射对其进行调用.

假设我们具有以下接口:

public interface IComponent
{
}

public interface IMessage
{
};

我们还创建Handles属性,该属性使我们可以将方法标记为处理特定的消息类型:

[AttributeUsage(AttributeTargets.Method)]
public class HandlesAttribute : Attribute
{
    public Type MessageType { get; private set; }

    public HandlesAttribute(Type messageType)
    {
        MessageType = messageType;
    }
};

现在,我们将创建一个消息代理,该代理将负责在给定的组件列表中查找所有消息处理方法.我们将使用反射来做到这一点.首先,我们将找到具有Handles属性的所有方法,然后检查它们是否具有必需的单个IMessage参数:

public class MessageBroker
{
    // Encapsulates a target object and a method to call on that object.
    // This is essentially our own version of a delegate that doesn't require
    // us to explicitly name the type of the arguments the method takes.
    private class Handler
    {
        public IComponent Component;
        public MethodInfo Method;
    };

    private Dictionary<Type, List<Handler>> m_messageHandlers = new Dictionary<Type, List<Handler>>();

    public MessageBroker(List<IComponent> components)
    {
        foreach (var component in components)
        {
            var componentType = component.GetType();

            // Get all private and public methods.
            var methods = componentType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            foreach (var method in methods)
            {
                // If this method doesn't have the Handles attribute then ignore it.
                var handlesAttributes = (HandlesAttribute[])method.GetCustomAttributes(typeof(HandlesAttribute), false);
                if (handlesAttributes.Length != 1)
                    continue;

                // The method must have only one argument.
                var parameters = method.GetParameters();
                if (parameters.Length != 1)
                {
                    Console.WriteLine(string.Format("Method {0} has too many arguments", method.Name));
                    continue;
                }

                // That one argument must be derived from IMessage.
                if (!typeof(IMessage).IsAssignableFrom(parameters[0].ParameterType))
                {
                    Console.WriteLine(string.Format("Method {0} does not have an IMessage as an argument", method.Name));
                    continue;
                }

                // Success, so register!
                RegisterHandler(handlesAttributes[0].MessageType, component, method);
            }
        }
    }

    // Register methodInfo on component as a handler for messageType messages.
    private void RegisterHandler(Type messageType, IComponent component, MethodInfo methodInfo)
    {
        List<Handler> handlers = null;
        if (!m_messageHandlers.TryGetValue(messageType, out handlers))
        {
            // If there are no handlers attached to this message type, create a new list.
            handlers = new List<Handler>();
            m_messageHandlers[messageType] = handlers;
        }

        var handler = new Handler() { Component = component, Method = methodInfo };
        handlers.Add(handler);
    }
}

上面的构造函数会记录一条警告消息,并忽略任何与我们要求的签名不匹配的方法(即,一个从IMessage派生的参数).

现在,让我们添加一个处理消息的方法.这将使用Invoke调用所有已注册的处理程序:

    // Passes the given message to all registered handlers that are capable of handling this message.
    public void HandleMessage(IMessage message)
    {
        List<Handler> handlers = null;
        var messageType = message.GetType();
        if (m_messageHandlers.TryGetValue(messageType, out handlers))
        {
            foreach (var handler in handlers)
            {
                var target = handler.Component;
                var methodInfo = handler.Method;

                // Invoke the method directly and pass in the method object.
                // Note that this assumes that the target method takes only one parameter of type IMessage.
                methodInfo.Invoke(target, new object[] { message });
            }
        }
        else
        {
            Console.WriteLine(string.Format("No handler found for message of type {0}", messageType.FullName));
        }
    }
};

现在,为了测试它,我们将使用这些示例消息和组件.我还添加了一些配置错误的测试方法(即错误的参数):

public class ExampleMessageA : IMessage
{
};

public class ExampleMessageB : IMessage
{
};

public class ExampleComponent : IComponent
{
    [Handles(typeof(ExampleMessageA))]
    public void HandleMessageA(ExampleMessageA message)
    {
        Console.WriteLine(string.Format("Handling message of type ExampleMessageA: {0}", message.GetType().FullName));
    }

    [Handles(typeof(ExampleMessageB))]
    public void HandleMessageB(ExampleMessageB message)
    {
        Console.WriteLine(string.Format("Handling message of type ExampleMessageB: {0}", message.GetType().FullName));
    }

    [Handles(typeof(ExampleMessageA))]
    public void HandleMessageA_WrongType(object foo)
    {
        Console.WriteLine(string.Format("HandleMessageA_WrongType: {0}", foo.GetType().FullName));
    }

    [Handles(typeof(ExampleMessageA))]
    public void HandleMessageA_MultipleArgs(object foo, object bar)
    {
        Console.WriteLine(string.Format("HandleMessageA_WrongType: {0}", foo.GetType().FullName));
    }
}

最后将所有内容整合在一起:

var components = new List<IComponent>() { new ExampleComponent() };
var messageBroker = new MessageBroker(components);

// A message has been received and deserialised into the correct type.
// For prototyping here we will just instantiate it.
var messageA = new ExampleMessageA();
messageBroker.HandleMessage(messageA);

var messageB = new ExampleMessageB();
messageBroker.HandleMessage(messageB);

您应该获得以下输出:

Method HandleMessageA_WrongType does not have an IMessage as an argument
Method HandleMessageA_MultipleArgs has too many arguments
Handling message of type ExampleMessageA: Program+ExampleMessageA
Handling message of type ExampleMessageB: Program+ExampleMessageB

您可以使用的小提琴是here.

为了提高方法调用性能,您可以使用here中提到的技术重写MethodInfo.Invoke.

标签:messagebroker,message-queue,deserialization,c
来源: https://codeday.me/bug/20191109/2010677.html