我有一种消息代理类,可在运行时将传入的消息类型映射到处理程序方法。对我而言,在处理程序中维护强类型消息非常重要。它的工作原理是找到所有从IMessage
继承的类,并创建一个Delegate
来调用任何具有Handles(TMessage)
属性的方法,其中TMessage
与传入的{{1 }}基础IMessage
。
因此,“处理程序”如下所示:
Type
同样,我真的很想避免在方法签名中使用[Handles(typeof(TestMessage))]
public void HandleTestMessage(objectsender, TestMessage request)
{
var response = new TestResponse() { TestInt = request.TestInt };
msgService.Send(sender, response);
}
,对于我来说,不必在方法主体中强制转换IMessage
至关重要。
broker类使用以下方法发现并注册处理程序:
IMessage
最后,当收到消息时,它使用此方法来调用(/// <summary>
/// Subscribes all methods with the <see cref="HandlesAttribute"/> to the given <see cref="IMessage"/> <see cref="Type"/>
/// </summary>
/// <param name="target">The object to inspect</param>
public void SubscribeAll(object target)
{
var targetType = target.GetType();
// Get all private and public methods.
var methods = targetType.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 2 arguments.
var parameters = method.GetParameters();
if (parameters.Length != 2)
{
log.LogDebug(string.Format("Method {0} has too many arguments", method.Name));
continue;
}
// The second argument must be derived from IMessage.
if (!typeof(IMessage).IsAssignableFrom(parameters[1].ParameterType))
{
log.LogDebug(string.Format("Method {0} does not have an IMessage as it's second argument", method.Name));
continue;
}
Type genericDelegate;
if(method.ReturnType == typeof(void))
{
genericDelegate = typeof(Action<,>).MakeGenericType(parameters[0].ParameterType, handlesAttributes[0].MessageType);
}
else
{
genericDelegate = typeof(Func<,,>).MakeGenericType(parameters[0].ParameterType, handlesAttributes[0].MessageType, method.ReturnType);
}
var handler = method.CreateDelegate(genericDelegate, target);
// Success, so register!
Subscribe(
handlesAttributes[0].MessageType,
handler);
}
}
)处理程序:
DynamicInvoke
当我将/// <summary>
/// Passes a given <see cref="IMessage"/> and optional sender to any <see cref="Handler"/>s accepting the message's underlying type
/// </summary>
/// <param name="message">The <see cref="IMessage"/> to send</param>
/// <param name="sender">The original sender of the message</param>
private void HandleMessage(IMessage message, object sender = null)
{
var messageType = message.GetType();
if (messageHandlers.TryGetValue(messageType, out List<Delegate> handlers))
{
foreach (var handler in handlers)
{
handler.DynamicInvoke(new object[] { sender, message });
}
}
else
{
log.LogError(string.Format("No handler found for message of type {0}", messageType.FullName));
throw new NoHandlersException();
}
}
转换为MethodInfo
(Delegate
或Action<,>
)时,我以为自己很聪明,但是我忽略了使用{{ 1}}超过Func<,,>
。事实证明DynamicInvoke
产生了相当大的开销。所以我的问题是,如何将处理程序Invoke
当作它们是 actual DynamicInvoke
(再次是Action <,>或Func <,,>)
如您所见,在Delegate
中,我知道知道Delegate
的基础类型,并且发件人始终是对象。基本上,我需要做类似的事情:
HandleMessage
显然这是不可能的,但它说明了我正在努力实现的目标(我认为)。
我尝试创建一种介于两者之间的通用方法:
message
但是在尝试调用此方法时,我只是碰到了底线问题。