3

I realise this has been asked before, but I didn't find a clear agreement on the best solution.

Is using dynamic (like below) the best way to do this? I guess its best to avoid dynamic whenever possible to help catch problems at compile-time.

(classA1 and classA2 implement Interface A and likewise for B)

 public static void Foo(InterfaceA a, InterfaceB b) 
 { 
    Foo((dynamic)a, (dynamic)b); 
 }

static void Foo(classA1 a, classB1 b) {  //some code }
static void Foo(classA2 a, classB2 b) {  //some code }
static void Foo(classA1 a, classB2 b) {  //some code }
static void Foo(classA2 a, classB1 b) {  //some code }

Or similarly...

public static void Foo(InterfaceA a, InterfaceB b) 
 { 
    ((dynamic) a).Foo(b); 
 }

public classA1
{
     void Foo(classB1 b) {  //some code }
}
//repeated for other cases    
gwizardry
  • 501
  • 1
  • 6
  • 19
  • You could remove one of the `(dynamic)` casts by making use of standard dynamic dispatch on one of the classes (i.e. having the call be `a.Foo(Interface B)`). After that, you can either use `dynamic` (which would work here) or implement the visitor pattern. – dlev Mar 22 '12 at 18:18
  • What are you trying with this pattern? I think there are better patterns that can solve your problems then using dynamics. Have you tested iff the code works that way? – Tarion Mar 22 '12 at 18:18

4 Answers4

3

Is using dynamic (like below) the best way to do this?

Well it's a way to do it - so long as the execution-time types will always end up with something that overload resolution will be happy with.

You might want to put a backstop method of

static void Foo(object x, object y)

in case none of the methods are applicable (e.g. a is a non-ClassA1/ClassA2 implementation). It's not going to help you if both values are null, mind you...

I would usually attempt to redesign so that this isn't required, but it's hard to know the best solution without more context.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I'm confident the overload types will always be of the right types in this case. I'd just read in a few places that with dynamic, using the visitor pattern is perhaps not required for double-dispatch anymore. – gwizardry Mar 22 '12 at 18:22
  • The context is my Foo is Intersection and my classes are various geometrical objects. Not sure that helps. I'm happy to redesign if the current wisdom is to stick to the traditional visitor pattern. – gwizardry Mar 22 '12 at 18:28
0

Are 'classA1' and so on implementations of InterfaceA? If so then why not just declare the Foo functions as accepting InterfaceA and InterfaceB and cast them within to the concrete implementation expected by the function? E.g.,

static void Foo(InterfaceA a, InterfaceB b) {
    classA1 c1 = a as classA1;
    classB1 b1 = b as classB1;
    // ... etc
}

Dynamic isn't intended to be used this way.

kprobst
  • 16,165
  • 5
  • 32
  • 53
  • 1
    The intention is that the call to the 'overloaded' Foo's accepting the class type parameters depend upon the underlying type of a and b – gwizardry Mar 22 '12 at 18:18
0

C# has traditionally been a statically-typed language. The dynamic keyword adds dynamic typing to the language. The usual advice is to use "dynamic" sparingly. Here may be a case where you need it.

Generics won't cut it as this won't compile:

    private void button1_Click(object sender, EventArgs e)
    {
        Foo(new classA1(), new classB2());
    }

    static void Foo<T, T1>(T a, T1 b) where T: InterfaceA
        where T1: InterfaceB
    {
        Foo2(a, b);
    }

    static void Foo2(classA1 a, classB1 b) { }
    static void Foo2(classA2 a, classB2 b) { }
    static void Foo2(classA1 a, classB2 b) { }
    static void Foo2(classA2 a, classB1 b) { }

    interface InterfaceA { }
    interface InterfaceB { }

    class classA1 : InterfaceA { }
    class classA2 : InterfaceA { }

    class classB1 : InterfaceB { }
    class classB2 : InterfaceB { }
Community
  • 1
  • 1
Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
0

You could do something AWFUL with reflection - but I'm sure it's not better than doing dynamic:

void Main()
{
    var a = "hello";//5;
    var b = "hello"; 

    var type1 = a.GetType();
    var type2 = b.GetType();

    var t = typeof(FooClass);

    var methods = t.GetMethods();

    foreach(var method in methods)
    {
        var parameters = method.GetParameters();

        if(parameters.Length == 2)
        {
            if(parameters[0].ParameterType == type1 
               && parameters[1].ParameterType == type2)
            {
                method.Invoke(this, new object[]{ a, b });
            }
        }
    }
}

public static class FooClass
{
    public static void Foo(int i, string s)
    {
        "Foo1".Dump();
    }

    public static void Foo(string s, string s2)
    {
        "Foo2".Dump();
    }
}
Dave Bish
  • 19,263
  • 7
  • 46
  • 63