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:
if (test is ClazzB) return GetDescription( (ClazzB) test );
or
Both of these require inspection of the object to determine its type
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"
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With