我有一种情况,我想覆盖基类的方法,以便稍微改变方法的返回类型。通过稍微改变,我的意思是返回一个继承自对象的对象,该对象将由基类型中的方法返回...实际上,一些代码会使这更容易...
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";
}
}
我遇到的问题是obj2的实例不会调用它的GetExternalObject()版本,而是使用它的父实现。
我认为这样做是因为在代码中
var obj = GetExternalObject();
obj的类型应该是父类中的ExternalObject。但我理解C#无法区分基于返回类型的方法。
我知道这个问题还有其他解决办法,比如定义一个IExternalObject,所以不要太过于沉溺于此。所有我想知道的是,是什么想法阻止子类GetExternalObject被子类本身调用?
或者我做的事情完全是愚蠢的? : - )
答案 0 :(得分:7)
或者我做的事情完全是愚蠢的? : - )
是的,你是。您无法通过覆盖方法来更改方法的返回类型。无论如何,我在你的样本中都不明白。只需保留返回类型,然后返回一个新的ExternalObjectStub
。这是有效的,因为ExternalObjectStub
来自ExternalObject
。
通过使用new
隐藏基本成员来更改返回类型通常是一个非常糟糕的主意,因为它会导致无法以多态方式使用的类。这正是您在此遇到的情况:如果包含引用的变量类型为ParentClass
类型,则它会调用ParentClass
中的方法,即使该实例的类型为ChildClass
},因为ChildClass
没有提供GetExternalObject
的重写实现。
答案 1 :(得分:4)
您使用它时的多态性是不正确的。您需要在子类中创建一个新方法,该方法使用新的返回类型隐藏基类的实现。您无法使用虚拟方法来重载您正在执行的方法。
虚拟方法用于在子类中创建方法的不同实现,而不是像您尝试的那样“重载”它。
通过更改参数而不是返回类型来完成方法的重载。
因此要么在子类中隐藏父方法,要么使用其他名称创建方法。使用虚拟功能不起作用。
答案 2 :(得分:3)
我不认为你做任何蠢事。我发现自己经常寻找实现相同模式的方法(Class Foo有一个类型为Bar的属性,类FooSub:Foo应该能够将该属性公开为类型为BarSub:Bar)。
了解" new"运算符是它只隐藏子类本身的实现。如果将子类强制转换回基类,则使用基类的实现。所以你要确保你有一个真实的"覆盖的方法,即使发生这种情况,它仍然会返回正确的类型。
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();
}
}
答案 3 :(得分:2)
您应该让您的类返回一个接口,每个类(ParentClass
和ChildClass
)返回一个接口实例。您还应该覆盖GetExternalObject
中的ChildClass
方法,以便v-table指向正确的实现。
此外,您的代码有一个拼写错误 - 当您拨打Main
时,您的obj
方法引用ShowYourHand
两次。我也改变了这一点,以引用obj
和obj2
。以下是如何使用界面实现此功能(并修复Main中的obj
拼写错误):
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";
}
}
答案 4 :(得分:0)
如果要在子类中返回ExternalObjectStub,则此ExternalObjectStub应从ExternalObject类派生