0

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!

1
Contributor
1
Reply
3
Views
5 Years
Discussion Span
Last Post by bunnyboy
0

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?

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.