如何在运行时将基类强制转换为派生类。 我想要做的是以下内容:我需要一个能够容纳订阅的系统, 在某种类型的消息中,有一个已分配的订阅者。当收到消息时,它将被转发到该系统,如果有这种消息的任何订户,它将进行查找,如果是,则将消息转发给订户。我有一个名为Response的抽象基类,后跟一个抽象层,例如。 AFResponse:响应,然后有实际(具体)实现,例如。 响应(摘要):AFResponse(摘要):AFIncommingMessage(具体)。
当收到数据包时,它会转到MessageBuilder,它会解析消息类型并构建它,如下所示:
public static T Build<T>(Packet packet) where T : Response
{
Type resolvedType;
if (!dependencyMap.TryGetValue(packet.MessageId, out resolvedType))
{
var str = String.Format("Could not resolve message. Message info: CMD0: {0}, CMD1: {1}, MessageID: {2}",
packet.Cmd0, packet.Cmd1, packet.MessageId);
Debug.WriteLine(str);
throw new ResolutionFailedException(str);
}
ConstructorInfo firstConstructor = resolvedType.GetConstructors().First();
return (T) firstConstructor.Invoke(new object[] {packet});
}
然后,如果消息是异步的,它将被转发到MessageBinder,它保存消息类型的订阅。
private void OnAsyncResponseReceived(Response response)
{
messageBinder.Forward(response);
}
MessageBinder类的实现如下:
public class MessageBinder
{
private class Subscriber<T> : IEquatable<Subscriber<T>> where T : Response
{
...
}
private readonly Dictionary<Type, IEnumerable> bindings;
public MessageBinder()
{
this.bindings = new Dictionary<Type, IEnumerable>();
}
public void Bind<TResponse>(ushort shortAddress, Action<ZigbeeAsyncResponse<TResponse>> callback)
where TResponse : Response
{
HashSet<Subscriber<TResponse>> subscribers = this.GetSubscribers<TResponse>();
if (subscribers != null)
{
subscribers.Add(new Subscriber<TResponse>(shortAddress, callback));
}
else
{
var subscriber = new Subscriber<TResponse>(shortAddress, callback);
this.bindings.Add(typeof(TResponse), new HashSet<Subscriber<TResponse>> { subscriber });
}
}
public void Forward<TResponse>(TResponse response)
where TResponse : Response
{
var subscribers = this.GetSubscribers<TResponse>();
if (subscribers != null)
{
Subscriber<TResponse> subscriber;
var afResponse = response as AFResponse;
if (afResponse != null)
{
subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
}
else
{
subscriber = subscribers.FirstOrDefault();
}
if (subscriber != null)
{
Debug.WriteLine("Forwarding received async response of type " + response.GetType().Name);
subscriber.Forward(response);
}
}
}
private HashSet<Subscriber<TResponse>> GetSubscribers<TResponse>() where TResponse : Response
{
IEnumerable subscribers;
this.bindings.TryGetValue(typeof(TResponse), out subscribers);
return (HashSet<Subscriber<TResponse>>)subscribers;
}
}
当调用MessageBinder类的GetSubscribers()方法时会出现问题,因为TResponse的类型是基类的类型,它是Response,因此找不到订阅者。所以我认为我需要做的是以某种方式将实际类型的消息传递给Forward方法,因此该类型将是实际的响应类型。
我改变了这样的方法:
private void OnAsyncResponseReceived(Response response)
{
messageBinder.Forward((dynamic)response);
}
它确实有效,但不知何故我有更好的(?)方式来做这个......或者这是真正的唯一解决方案吗?也许我应该改变整体设计,我不知道......我第一次遇到这类问题,这就是我想出来的。
我愿意接受建议,批评,当然还有最好的解决方案:) 我很好奇,如果它与我的解决方案不同且更有效,你将如何实现它。 谢谢你的帮助!
答案 0 :(得分:1)
您是否考虑通过邮件本身路由订阅者请求?它不一定需要是直接的 - 您的基类可能需要子类覆盖暴露某种实用程序对象的属性。实用程序对象可以强类型化为消息类型,并且可以进行强类型调用以获取订阅者。
您仍然可以使用您创建的方法(用于获取订阅者) - 实用程序类只能进行强类型调用。
编辑:
这个怎么样?
private void OnAsyncResponseReceived<T>(T response) where T : Response {
messageBinder.Forward(response);
}
原始代码的问题在于响应变量是强类型的。投射到动态的“非强类型”它。通过使用通用方法,就像在整个应用程序中一样,通过添加的约束,泛型类型必须从Response继承,您可以获得相同的结果,但内置动态类型。但老实说,我认为投入动态是相当聪明的。 :)