1

Is it possible in C# to overload a method with different signatures in different descendants of a base class, and then call the appropriate method based on the runtime type of an argument to the overloaded method?

As background, I'm working on message handling process, starting from a point where the message type not known at compile time, and with different message handling functionality intended to be added for different types over time. Ideally new message handling would change the subclass but not the base class. It works fine now by including all typed methods in the base class and explicitly selecting the correct one based on the message type. The question is driven mainly by curiosity since based on the answer to this question I expect it should be possible. If I'm overcomplicating this and theres a simpler approach I'd welcome that advice too.

Example code showing this is below, I expected HandleTypedMessage(BaseMessage msg) and HandleTypedMessage(MessageA msg) to be called once each, instead HandleTypedMessage(BaseMessage msg) is called twice.

class Program
{
    static void Main(string[] args)
    {
        var msga = new MessageA();
        var msgb = new MessageB();
        MessageHandler handlerA = new MessageHandlerA();

        handlerA.HandleTypedMessage((dynamic) msga);

        handlerA.HandleTypedMessage((dynamic) msgb);

        Console.ReadLine();
    }
}

public interface IHandlesMessage
{
    void HandleMessage(BaseMessage message);
}

public abstract class BaseMessage
{
    public abstract string MessageType { get; }
}

public class MessageA : BaseMessage
{
    public override string MessageType
    {
        get { return "http://message/a"; }
    }
}

public class MessageB : BaseMessage
{
    public override string MessageType
    {
        get { return "http://message/a"; }
    }
}

public abstract class MessageHandler
{
    public abstract void HandleTypedMessage(BaseMessage msg);
}

public class MessageHandlerA : MessageHandler
{
    public override void HandleTypedMessage(BaseMessage msg)
    {
        Console.WriteLine("Did nothing with " + msg.MessageType);
    }

    public void HandleTypedMessage(MessageA msg)
    {
        Console.WriteLine("Handled message a");
    }

}

public class MessageHandlerB : MessageHandler
{
    public override void HandleTypedMessage(BaseMessage msg)
    {
        throw new NotImplementedException();
    }

    public void HandleTypedMessage(MessageB msg)
    {
        System.Console.WriteLine("Handled message b");
    }
}
Community
  • 1
  • 1
Rattle
  • 2,393
  • 2
  • 20
  • 25

2 Answers2

2

You're making incorrect part of your method invocation dynamic. Make the target dynamic, not the parameter:

((dynamic)handlerA).HandleTypedMessage(msga);
((dynamic)handlerA).HandleTypedMessage(msgb);

It will look for best matching HandleTypedMessage method for actual type handlerA points to (MessageHandlerA), not the one it's declared as (MessageHandler).

Code above prints

Handled message a
Did nothing with http://message/a
MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • Perfect. Just noticed I left the same MessageType property in both message classes but it's clear what is happening – Rattle Feb 15 '14 at 01:07
1

One approach is to move the dynamic dispatch into the handler sublcasses like this:

public class MessageHandlerA : MessageHandler {
    public override void HandleTypedMessage(BaseMessage msg) {
        this.HandleTypedMessageCore((dynamic) msg);
    }

    private void HandleTypedMessageCore(BaseMessage msg) {
        Console.WriteLine("Did nothing with " + msg.MessageType);
    }

    private void HandleTypedMessageCore(MessageA msg) {
        Console.WriteLine("Handled message a");
    }
}
recursive
  • 83,943
  • 34
  • 151
  • 241