views:

28

answers:

1

I have a set of messages, that all derive from Message, and implement the interface IMessage:

class Message : IMessage
{
   public string example1;
}

class LogOnMessage : Message
{
   public string password;
}

class LogOffMessage : Message
{
   public string anotherExample;
}

I have a messaging system, in which a proxy is able to detect incoming messages, and depending on the type of message, runs a delegate of the form

void MyDelegate(IMessage msg)

This is done by calling a handler registration function like this:

RegisterHandler(typeof(LogoffMessage),handler)

The implementation for this is simply a dictionary of the types, with each entry being a list of handlers: Dictionary< type, list< MyDelegate > >

Now, this is all fine, but of course the handler looks like this:

void MyHandler(IMessage msg)
{
   LogOffMessage lMsg = (LogOffMessage)msg;
   string athrEx = lMsg.anotherExample;

//Proceed with handling the logoff
}

Now, there's a couple of issues with this. One is that you might try to convert the Msg into the wrong type.

The other is that it's a bit inelegant to have to unwrap everything in the first few lines of any handler, rather than having handlers like this, which I'd like to have:

void MyDelegate(LogOffMessage msg)

What suggestions would you make?

I was thinking maybe instead of a dictionary of types, some other structure could be used to hold different delegate types, using generics. So there would be a MyDelegate < MsgType > list for each type. But it's not clear how to make a collection of collections, where each inner collection type is different.

+1  A: 

Make your delegate generic:

delegate void MessageHandler<T>(T message) where T : IMessage

And make your RegisterHandler method generic:

void RegisterHandler<T>(MessageHandler<T> handler)

then call it with either:

RegisterHandler(handler); // Let type inference handle it
RegisterHandler<LogOffMessage>(handler); // State type argument explicitly

Then when you fetch the delegate by type, you can cast it to the right handler type - even though it's "unsafe" from the compiler's point of view, you know that you'll only have added the right kind of handler to the dictionary:

// When you populate this, create the appropriate type of
// `List<MessageHandler<T>>` based on the key
private Dictionary<Type, object> handlerMap;

void CallHandlers<T>(T message) where T : IMessage
{
    object value;
    if (handlerMap.TryGetValue(typeof(T), out value))
    {
        List<MessageHandler<T>> handlers = (List<MessageHandler<T>>) value;
        foreach (MessageHandler<T> handler : handlers)
        {
            handler(message);
        }
    }
}

Note that another alternative to using a List<MessageHandler<T>> is just to combine delegates and end up with a multicast delegate in the map for each type.

Jon Skeet