Method override for changing return type

I have a situation where I want to override the method of the base class in order to slightly change the return type of the method. By a small change, I mean returning an object that inherits from an object that would be returned by the method in the base type ... in fact, a little code will simplify this ...

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 problem is that the obj2 instance does not call its version of GetExternalObject (), but rather uses its parent implementation.

I think it is, because in the code

 var obj = GetExternalObject(); 

It is assumed that the obj type will be ExternalObject in the parent class. However, I realized that C # cannot distinguish between methods based on return type.

I know that there are other solutions to the problem, such as defining an IExternalObject, so don't get too stuck with this. All I wanted to know is what it thinks that the GetExternalObject child classes are not called even by the child class itself?

Or am I doing something completely stupid ?:-)

+6
source share
5 answers

Or am I doing something completely stupid ?:-)

Yes you. You cannot change the return type of a method by overriding it. In any case, I do not understand this. Just leave the return type as it is and return a new ExternalObjectStub . This works because ExternalObjectStub comes from ExternalObject .

Changing the return type by hiding the base element with new , as you do, is usually a very bad idea, because it leads to a class that cannot be used in a polymorphic way. This is exactly what you are experiencing here: if the type of the variable containing the link is of type ParentClass , it calls the method in ParentClass , even if the instance does have type ChildClass , because ChildClass does not provide an excessive implementation of GetExternalObject .

+7
source

Polymorphism when you use it is wrong. You need to create a new method in your child class that hides the implementation of the base class with a new return type. You cannot use virtual methods to overload a method, as you do.

Virtual methods are used to create another implementation of the method in the child class, and not to "overload" as you are trying to do.

Method overloading is done by changing the parameters, not the return type.

Thus, either hide the parent method, either in the child class, or create a method with a different name. Using virtual for this will not work.

+4
source

You must return an interface to your classes, each of which ( ParentClass and ChildClass ) returns an instance of the interface. You must also override the GetExternalObject method in ChildClass so that the v-table points to the correct implementation.

Also, your code had a typo - your Main method referenced by obj twice when you called ShowYourHand . I also changed this to reference obj and obj2 . Here you can implement this using the interface (and commit the typo of obj 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"; } } 
+2
source

I don’t think you are doing anything at all. I very often look for ways to implement this same template (the Foo class has a property of type Bar, the class FooSub: Foo should be able to open this property as a type of BarSub: Bar).

One important thing to understand about the β€œnew” operator is that it hides the implementation directly in the subclass. If a subclass is discarded back to the base class, the base class implementation is used instead. That way, you'll want to make sure that you have a β€œreal” method to override, so that even when this happens, it will still return the correct 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(); } } 
+2
source

If you want to return ExternalObjectStub in the child class, this ExternalObjectStub must be obtained from the ExternalObject class

0
source

Source: https://habr.com/ru/post/896480/


All Articles