ima*_*ady 3 c# generics dependency-injection type-inference asp.net-core
我正在处理一个将 XML 消息发布到我的服务器的微信项目。该消息可以是多种类型中的任何一种。因此,我首先将消息反序列化为具有基“WxMessage”的相应对象,然后将该对象返回给调度程序,调度程序将找到正确的消息处理程序来处理该消息。每种消息类型对应的处理程序都通过 IWxMessageHandler<> 接口注册到 Asp.net core 2.1 的 DependencyInjection 中。
services.AddScoped<IWxMessageHandler<WxMessage_Image>, WxMessageHandler_Image>();
services.AddScoped<IWxMessageHandler<WxMessage_Text>, WxMessageHandler_Text>();
services.AddScoped<IWxMessageHandler<WxMessage_File>, WxMessageHandler_File>();
Run Code Online (Sandbox Code Playgroud)
这是消息服务:
public async Task<string> Handle(string messageBody)
{
var reply = default(WxMessage);
using (var deserial = new WxInMessageDeserializer())
{
var deserializedModel = deserial.Parse(messageBody);
reply = (WxReplyMessage_Text) await HandleMessage(deserializedModel);
}
return await Task.FromResult(BuildXmlContent(reply));
}
public async Task<WxMessage> HandleMessage<TMessage>(TMessage message)
{
var _handler = _serviceProvider.GetService<IWxMessageHandler<TMessage>>();
......
}
Run Code Online (Sandbox Code Playgroud)
然而,它无法解析 IWxMessageHandler<> 的正确实现,因为 WxInMessageDeserializer 返回 WxMessage 的基础对象:
var result = default(WxMessage);
switch (_msgTYpe)
{
case "text": result = (WxMessage_Text)new XmlSerializer(typeof(WxMessage_Text))
.Deserialize(new StringReader(rawMessage));
break;
case "image": result = (WxMessage_Image)new XmlSerializer(typeof(WxMessage_Image))
.Deserialize(new StringReader(rawMessage));
break;
case "voice": result = (WxPushMessage_Voice)new XmlSerializer(typeof(WxPushMessage_Voice))
.Deserialize(new StringReader(rawMessage));
break;
......
case "file": result = (WxMessage_File)new XmlSerializer(typeof(WxMessage_File))
.Deserialize(new StringReader(rawMessage));
break;
}
return result;
Run Code Online (Sandbox Code Playgroud)
我想这是 C# 类型推断的限制,因为当我使用像 HandleMessage(somemessage) 这样的显式调用方法时,结果是正确的。目前,我正在通过旋转所有服务的列表来解决此问题,然后选择我想要的服务:
var type = typeof(TMessage);
var _handler = _handlers
.Where(h => h.GetHandlingType().Equals(message.GetType()))
.FirstOrDefault();
var result = _handler.Handle(message);
return await result;
Run Code Online (Sandbox Code Playgroud)
但我想知道是否有更好的解决方案来解决这个问题。谢谢如果有人可以帮忙。
问题在于泛型类型是在编译时评估的。因此,即使您的HandleMessage
类型是泛型HandleMessage<TMessage>(TMessage message)
,所使用的类型TMessage
也不是运行时类型。
你调用你的方法的方式是这样的:
\n\nvar deserializedModel = deserial.Parse(messageBody);\nreply = (WxReplyMessage_Text) await HandleMessage(deserializedModel);\n
Run Code Online (Sandbox Code Playgroud)\n\n因此,编译时类型deserializedModel
决定了泛型类型参数将使用什么类型TMessage
。
由于Parse
本身不是泛型,因此它可能会返回一个基类型,然后该基类型将用于HandleMessage
. 因此,内部HandleMessage
,TMessage
将是该基类型,并且调用GetService<IWxMessageHandler<TMessage>>
将使用该基类型来检索处理程序服务实例。
如果您想获取实际的处理程序,则必须从服务提供者处检索具体的服务类型。为了从消息对象的运行时类型执行此操作,您将需要使用反射来检索和构造该类型:
\n\npublic async Task<WxMessage> HandleMessage(IWxMessage message) \n{\n var messageType = message.GetType();\n var messageHandlerType = typeof(IWxMessageHandler<>).MakeGenericType(messageType);\n\n var handler = _serviceProvider.GetService(messageHandlerType);\n\n // \xe2\x80\xa6\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n这假设您有IWxMessage
消息的基本类型并使该HandleMessage
方法成为非泛型。相反,处理程序类型将在编译时从具体消息类型解析。请注意,您还应该为消息处理程序提供一些基本接口,该接口允许您调用所有类型的方法(最好只采用IWxMessage
),否则您还必须使用反射来调用处理程序。
归档时间: |
|
查看次数: |
5758 次 |
最近记录: |