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!

Actually the Convert.ChangeType is irrelevant here, as is only used for build-in types.

private void OnAsyncResponseReceived(Response response)
{
    messageBinder.Forward((dynamic)response);
}

It does work this way, but is this ok?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.