Overriding method to change return type
I have a situation in which I want to override a method of the base class in order to slightly change the return type of the method. By slightly change I mean return an object that inherits from the object that would have been returned by the method in the base type ... actually, a little code would make this easier ...
class Program
{
static void Main(string[] args)
{
var obj = new ParentClass();
Console.WriteLine("Parent says: " + obj.ShowYourHand());
var obj2 = new ChildClass();
Console.WriteLine("Child says: " + obj2.ShowYourHand());
Console.ReadLine();
}
}
public class ParentClass
{
public string ShowYourHand()
{
var obj = GetExternalObject();
return obj.ToString();
}
protected virtual ExternalObject GetExternalObject()
{
return new ExternalObject();
}
}
public class ChildClass : ParentClass
{
protected virtual new ExternalObjectStub GetExternalObject()
{
return new ExternalObjectStub();
}
}
public class ExternalObject
{
public override string ToString()
{
return "ExternalObject";
}
}
public class ExternalObjectStub : ExternalObject
{
public override string ToString()
{
return "ExternalObjectStub";
}
}
The issue I have is that the instance of obj2 doesn't call it's version of GetExternalObject() but rather uses it's parent's implementation.
I think that it is doing so because in the code
var obj = GetExternalObject();
the type of obj is expected to be ExternalObject in the parent class. I understood however that C# cannot distinguish between methods based on return type.
I know th开发者_运维问答ere are other solutions to the issue such as defining an IExternalObject so don't get too hung up about that. All I wanted to know was what the thinking is that prevents the child classes GetExternalObject from being called even by the child class itself?
Or am I doing something totally daft? :-)
Or am I doing something totally daft? :-)
Yes, you are. You can't change the return type of a method by overriding it. I don't understand it in your sample anyway. Just leave the return type as it was and return a new ExternalObjectStub
. This works, because ExternalObjectStub
derives from ExternalObject
.
Changing the return type by hiding the base member with new
as you do it, is generally a very bad idea, because it leads to a class that can't be used in a polymorphic way. This is exactly what you are experiencing here: If the type of the variable that holds the reference is of type ParentClass
it calls the method in ParentClass
, even if the instance really is of type ChildClass
, because ChildClass
doesn't provide an overriden implementation of GetExternalObject
.
Polymorphism as you're using it, is not correct. You need to create a new method in your child class that hides the implementation of the base class, with the new return type. You cannot use virtual methods to overload a method like you're doing.
Virtual methods are used to create a different implementation of a method in the child class, not to "overload" it like you're trying to do.
Overloading of methods is done by changing the parameters, not the return type.
So either hide the parent method, in the child class, or create a method with another name. Using virtual for this will not work.
I don't think you're doing anything daft at all. I find myself very frequently looking for ways to implement this same pattern (Class Foo has a Property of type Bar, Class FooSub : Foo should be able to expose that Property as being of type BarSub : Bar).
One important thing to understand about the "new" operator is that it only hides the implementation on the subclass itself. If the subclass is cast back to the base class, the base class's implementation is used instead. So you'll want to make sure you have a "real" method to override so that even when this happens, it's still returning the proper type.
public class ParentClass
{
public string ShowYourHand()
{
var obj = GetExternalObject();
return obj.ToString();
}
protected ExternalObject GetExternalObject()
{
return this.RealGetExternalObject();
}
protected virtual ExternalObject RealGetExternalObject()
{
return new ExternalObject();
}
}
public class ChildClass : ParentClass
{
new protected ExternalObjectStub GetExternalObject()
{
return (ExternalObjectStub)this.RealGetExternalObject();
}
protected override ExternalObject RealGetExternalObject()
{
return new ExternalObjectStub();
}
}
You should have your classes return an interface, with each class (ParentClass
and ChildClass
) returning an instance of the interface. You should also override the GetExternalObject
method in your ChildClass
so that the v-table points to the right implementation.
Also, your code had a typo -- your Main
method referenced obj
twice when you were calling ShowYourHand
. I changed that, also, to reference obj
and obj2
. Here's how you can implement this with an interface (and fixing the obj
typo in Main):
class Program
{
static void Main(string[] args)
{
var obj = new ParentClass();
Console.WriteLine("Parent says: " + obj.ShowYourHand());
var obj2 = new ChildClass();
Console.WriteLine("Child says: " + obj2.ShowYourHand());
Console.ReadLine();
}
}
public class ParentClass
{
public string ShowYourHand()
{
var obj = this.GetExternalObject();
return obj.ToString();
}
protected virtual IExternalObject GetExternalObject()
{
return new ExternalObject();
}
}
public class ChildClass : ParentClass
{
protected override IExternalObject GetExternalObject()
{
return new ExternalObjectStub();
}
}
public interface IExternalObject { }
public class ExternalObject : IExternalObject
{
public override string ToString()
{
return "ExternalObject";
}
}
public class ExternalObjectStub : IExternalObject
{
public override string ToString()
{
return "ExternalObjectStub";
}
}
If you want to return ExternalObjectStub in the child class this ExternalObjectStub should derive from ExternalObject class
精彩评论