Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloaded Method..why is base class given precedence?

Tags:

c#

c#-4.0

In the following code, i have an overloaded method, one that takes a parameter of type ClazzA and the other of type ClazzB. In the code shown, the first GetDescription method (the one that takes ClazzA as a parameter) is called. I think i understand why.
My question is..is there an elegant way of having the method that takes clazzB called first if the underlying object is of type classB (without having to inspect each object and casting it to clazzB)?

public class ClazzA
{
    public virtual string Descr { get { return "A"; } }
}

public class ClazzB : ClazzA
{
    public override string Descr { get { return "B"; } }
}

public static class test
{
    public static void Main()
    {
        ClazzA test = new ClazzB();
        GetDecription(test);
    }

    public static void GetDecription(ClazzA someClazz)
    {
        Debug.WriteLine("I am here");
    }

    public static void GetDecription(ClazzB someClazz)
    {
        Debug.WriteLine("I want to be here");
    }
}

Output: "I am here"

I really want the 2nd method to be called since 'test' is of type ClassB. Curerently the only two solutions i have is:

  1. if (test is ClazzB) return GetDescription( (ClazzB) test );

or

  1. In ClassA do pretty much the same thing...check the type and delegate to the 2nd method

Both of these require inspection of the object to determine its type

like image 911
mike01010 Avatar asked Oct 26 '12 23:10

mike01010


2 Answers

Overloads are determined at compile time. The compile time type of the reference is ClazzA so that overload is chosen. What you are asking for is related to multiple dispatch. C# and many other languages like C++ and Java only support single dispatch (via virtual methods). There are a number of ways people have come up with to work around this. The purest OO way of doing this is the visitor pattern. You modify the classes to contain a method (Accept) which then passes the this reference to a method on the visitor (Visit). This works because you override the Accept method in each subclass so that this will be the object's actual type. All the visitor needs is a specific method for each subclass that you want to support (see wikipedia for more details).

A sample:

public class ClazzA
{
   public virtual string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public class ClazzB : ClazzA
{
   public override string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public abstract class ClassVisitor
{
  public abstract string Visit(ClazzA a);
  public abstract string Visit(ClazzB b);
}

public class GetDescriptionVisitor : ClassVisitor
{
    public override string Visit(ClazzA a)
    {
       return "A";
    }

    public override string Visit(ClazzB b)
    {
       return "B";
    }
}

Usage:

ClassVisitor visitor = new GetDescriptionVisitor();
ClazzA b = new ClazzB();
Console.WriteLine(b.Accept(visitor)); // prints "B"
like image 106
Mike Zboray Avatar answered Oct 03 '22 19:10

Mike Zboray


Because the method overload resolution occurs at compile-time. In terms of dealing with this situation, if you are using C# 4 then you could use dynamic so that the overload resolution is deferred to execution-time.

dynamic instance = new ClazzB();
Console.WriteLine(GetDescription(instance));

Alternatively, you could use a Visitor Pattern something like the following but this double-dispatch approach feels like a lot of work. Note the repetitive Visit method that must be re-implement in every derived type!

public interface IVisitable
{
    string Visit(DescriptionVisitor visitor);
}

public class ClazzA : IVisitable
{
    public virtual string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class ClazzB : ClazzA
{
    public override string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class DescriptionVisitor
{
    public string Visit(ClazzA item) { return "Description A"; }
    public string Visit(ClazzB item) { return "Description B"; }
}

Then the following will ultimately still call the overload in DescriptionVisitor that takes ClazzB.

var visitor = new DescriptionVisitor();
ClazzA a = new ClazzB();
Console.WriteLine(a.Visit(visitor));
like image 37
blins Avatar answered Oct 03 '22 18:10

blins