Multi-Dispatch in C#

As of C# version 4 you can do:

    interface IA { }
    interface IB { }
    class CA1 : IA {}
    class CA2 : IA {}
    class CA11 : CA1 {}
    class CB1 : IB {}
    class CB2 : IB {}

    class MD
    {
        public enum X { X } ;
        public static void Foo(IA a, IB b, X dispatch = X.X) { Foo((dynamic)a, (dynamic)b); }
        static void Foo(IA a, IB b) { Console.WriteLine("IA IB"); }
        static void Foo(CA1 a, CB1 b) { Console.WriteLine("CA1 CB1"); }
        static void Foo(CA2 a, CB1 b) { Console.WriteLine("CA2 CB1"); }
        static void Foo(CA1 a, CB2 b) { Console.WriteLine("CA1 CB2"); }
        static void Foo(CA2 a, CB2 b) { Console.WriteLine("CA2 CB2"); }
        static void Foo(CA11 a, CB2 b) { Console.WriteLine("CA11 CB2"); }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var a1 = new CA1();
            var a11 = new CA11();
            var a2 = new CA2();
            var b1 = new CB1();
            var b2 = new CB2();
            MD.Foo(a1, b1);
            MD.Foo(a2, b1);
            MD.Foo(a1, b2);
            MD.Foo(a2, b2);
            MD.Foo(a11, b1);
            MD.Foo(a11, b2);
        }
    }

Yes, it uses the evil "dynamic" keyword. The usage is safely hidden behind type safe methods so it's really not that evil.

The enum X is used to differentiate the catch all IA, IB method from the main mutli-dispatch handler. The catch all method is used when no combination of overloads matches. Without this the code would infinitely loop if past in a concrete type for which no overload method existed.