Jim Kramer
Jim Kramer

Reputation: 123

conditional logic based on type

Given:

interface I
{
}

class B: I
{
}

class C: I
{
}

class A
{

    public void Method(B arg)
    {
    }

    public void Method(C arg)
    {
    }

    public void Method(I arg)
    {
       // THIS is the method I want to simplify.
       if (I is B)
       {
          this.Method(arg as B);
       }
       else if (I is C)
       {
          this.Method(arg as C);
       }
    }
}

I know that there are better ways to design this type of interactions, but because of details which would take too long to explain this is not possible. Since this pattern will be duplicated MANY times, I would like to replace the conditional logic with a generic implementation which I could use just one line. I can't see a simple way to implement this generic method/class, but my instincts tell me it should be possible.

Any help would be appreciated.

Upvotes: 2

Views: 1796

Answers (6)

Vivek
Vivek

Reputation: 16508

interface I
{ 
} 

class B : I
{
}

class C : I
{
}    

class A 
{
    public void Method(B arg)
    {
        Console.WriteLine("I'm in B");
    }

    public void Method(C arg)
    {
        Console.WriteLine("I'm in C");
    }

    public void Method(I arg)
    {
        Type type = arg.GetType();

        MethodInfo method = typeof(A).GetMethod("Method", new Type[] { type });
        method.Invoke(this, new I[] { arg });
    }
}

Upvotes: 1

Ilya Ryzhenkov
Ilya Ryzhenkov

Reputation: 12142

What you want is double dispatch, and visitor pattern in particular.

Upvotes: 5

Jonathan Allen
Jonathan Allen

Reputation: 70307

Easy. In Visual Basic I do this all the time using CallByName.

Sub MethodBase(value as Object)
    CallByName(Me, "RealMethod", CallType.Method, value)

This will call the overload of RealMethod that most closely matches the runtime type of value.

I'm sure you can use CallByName from C# by importing Microsoft.VisualBasic.Interaction or by creating your own version using reflection.

Upvotes: 0

Jeffrey L Whitledge
Jeffrey L Whitledge

Reputation: 59463

This is kinda ugly but it gets the job done:

public void Method(B arg)
{
  if (arg == null) return;
...
}
public void Method(C arg)
{
  if (arg == null) return;
...
}

public void Method(I arg)
{
  this.Method(arg as B);
  this.Method(arg as C);
}

I don't think I would do it this way, though. It actually hurts looking at that. I'm sorry I forced you all to look at this as well.

Upvotes: 1

jop
jop

Reputation: 84043

I would put the method inside the interface and then let polymorphism decide which method to call

interface I
{
   void Method();
}

class B : I
{
   public void Method() { /* previously A.Method(B) */}
}

class C : I
{
   public void Method() { /* previously A.Method(C) */ }
}

class A
{
   public void Method(I obj)
   { 
     obj.Method();
   }
}

Now when you need to add a new class, you only need to implement I.Method. You don't need to touch A.Method.

Upvotes: 16

Marc Gravell
Marc Gravell

Reputation: 1062855

It doesn't exist in a convenient form withing C# - see here for an idea based on F#'s pattern matching, that does exactly what you want. You can do some things with reflection to select the overload at runtime, but that will be very slow, and has severe issues if anything satisfies both overloads. If you had a return value you could use the conditional operator;

return (I is B) ? Method((B)I) : ((I is C) ? Method((C)I) : 0);

Again - not pretty.

Upvotes: 0

Related Questions