1

I have a message class that holds the name of a destination, and a generic variable acting as the message payload

public class Message<T> {
    public string Destination
    public T Payload

    // ... constructors, functions, etc. ...
}

This way, I should be able to create messages of any type (without needing to explicitly list the types that are valid) and still have the compiler test the type-safety.

I want to create a method that generates a message. The problem is I don't want to return a specific kind of message, I just want to return any message, or perhaps an array/List of messages with different types. I.e. instead of explicitly returning Message<String> or Message<int> I just want it to return Message. That way I can pass the message to the destination, then the destination can figure out what the type is and consume the payload.

The problem is, if I do that, it'll involve a lot of typecasting and kinda defeats the point of using generics. I might as well just make Payload an object then typecast it in the destination. Or just use dynamic. Should I or shouldn't I use object or dynamic in this case, and why?

This is actually a rephrasing of a previous poorly-phrased question that I asked. Previously, instead of using a generic type, I would use dynamic for the message, but answers kept saying that using dynamic was unsuitable except for things like Python interop, and many were referring to the use of generics which at the time I didn't think was suitable because of the above reason. My previous question is here: When to not use dynamic in C#

EDIT: As suggested by Benjamin, here's what I'd like to happen

I have a "manager" that has a bunch of model objects in a dictionary. In it there's a function that goes like this:

void ManagerFunction()
{
    Message m = getMessage();
    objects[m.Destination].handleMessage(m)
}

Notice that the manager doesn't care about the payload.

This have a function generating a message and returning it.

Message getMessage()
{
    Message m;

    if(x)
        m = new Message<String>();
    else if(y)
        m = new Message<int>();
    // ... some more else-if's here ...
    else
        m  = new Message<double>();

    return m;
}

Notice how the message generating function can send a message of any type

Let's say that the destination all consist of objects that implement the following interface

interface DestinationInterface{
    void HandleMessage(Message m)
}

I would like it so that one object might handle the interface in the following way

void HandleMessage(Message m)
{
    if(m.Payload is String){
        String s = (String) m.Payload
        // ...
    } else throw Exception();
}

And another object that implements the interface might handle the interface in a different way

void HandleMessage(Message m)
{
    if(m.Payload is int)
    {
        int i = (int) m.Payload
        // ...
    } else throw Exception();
}

Because of the typecasting, a generic type wouldn't be useful. So should I just use object for the payload? Why not use dynamic and get rid of all the typecasting in the first place?

  • I only had a quick skim of the question, but is this along the lines of what you need? stackoverflow.com/questions/6623188/… – MetaFight Sep 18 '14 at 8:26
  • Somewhat. The problem is I would still need to typecast at the destination, so I don't see the point of using generics even with the answer of that question. – 9a3eedi Sep 18 '14 at 8:33
  • I think you need to give a more concrete example of the interface you'd like to write (which may fail to compile), and how you intend to use it, so that we can try to provide better suggestions – Benjamin Hodgson Sep 18 '14 at 8:46
  • Edited, Hope it's good now – 9a3eedi Sep 18 '14 at 9:08
2

You could skip using generic messages and instead use generic handlers.

Here is how we do it:

public interface IMessage
{
}

public interface IHandle<in T> where T : IMessage
{
    void Handle(T message);
}

Implement IMessage with "payload properties" that actually describes the payload.

public class ConcreteMessage : IMessage
{
    ...
    public int BetterNameThanPayload { get; private set; }
}

Implement a message handler:

public class ConcreteHandler : IHandle<ConcreteMessage>
{
    public void Handle(ConcreteMessage message) 
    {
        var foo = message.BetterNameThanPayload;
        ...
    }
}

Use dynamic keyword when calling the handler (this should be the only place to use 'dynamic'):

void ManagerFunction()
{
    IMessage m = getMessage();
    var messageHandlerType = typeof(IHandle<>).MakeGenericType(m.GetType());
    var messageHandler = [resolve message handler];
    ((dynamic)messageHandler).Handle((dynamic)message);
}

You could use a "destination" string to resolve the handler, but if using an IOC container it's simpler to resolve it by type (and it's type-safe). Also, skipping "destination" will decouple the message from the handler.

  • +1, this is exactly what I would've suggested (and indeed it's how we built our platform at my place of work) – Benjamin Hodgson Sep 18 '14 at 15:31
  • This seems like a potentially great solution, but you still end up using dynamic, as well as some reflection (getType). What I don't understand is why is it "ok" to use it in this case, but not in the message object case? And yes, "payload" is a terrible name :) sorry I couldn't think of a better name – 9a3eedi Sep 19 '14 at 1:13
  • 1
    Well, I don't think using dynamic is wrong per se, but it is not suitable to use in a message because the message can potentially be handled by many handlers and you will loose type checking at as many places. In the solution posted above, you will be using dynamic at one place (you should implement a message "bus" that takes care of resolving and executing handlers). That way, you hide the use of dynamic from the message API and makes life happier for everyone :) – jhdrn Sep 19 '14 at 5:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.