How can I cast a base class to a derived on at runtime. What I am trying to do is the following: I need a system which will hold the subscriptions, where a certain type of message, has an assigned Subscriber. When a message is received, it will bi forwarded to this system, which will do a lookup if there are any subscribers to this kind of message and if so, message is forwarded to subscriber. I have an abstract base class called Response, followed by an abstract layers eg. AFResponse : Response, and then there are the actual (concrete) implementation, eg. Response(abstract) : AFResponse(abstract) : AFIncommingMessage(concrete).
When the packet is received, it goes to MessageBuilder, which resolves the message type and build's it, like so:
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});
}
Then if message is async, it is forwarded to MessageBinder, which holds the subscriptions of message types.
private void OnAsyncResponseReceived(Response response)
{
messageBinder.Forward(response);
}
And a MessageBinder class is implemented as following:
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;
}
}
The problem arises when a method GetSubscribers() of MessageBinder class is called, because the type of TResponse is of type of a base class, which is Response and therefore no subscriber/s are/is found. So what I thought I need to do is somehow pass the actual type of message to Forward method, so that type would be the actual type of response.
I changed the method like so:
private void OnAsyncResponseReceived(Response response)
{
dynamic resolvedResponse = Convert.ChangeType(response, response.GetType());
messageBinder.Forward(resolvedResponse);
}
It does work, but somehow I thing there is a better(?) way to do this ... or is this the actual and only solution? Maybe I should change the overall design, I do not know ... I am facing this type of problem the first time and this is what I came up with.
I am open to suggestions, critic, and of course, the best possible solution :) I am curious to, how would you implement this, if it is different and more efficient than my solution. Thank you for any help!