覆盖更改返回类型的方法

时间:2011-09-02 11:28:01

标签: c#

我有一种情况,我想覆盖基类的方法,以便稍微改变方法的返回类型。通过稍微改变,我的意思是返回一个继承自对象的对象,该对象将由基类型中的方法返回...实际上,一些代码会使这更容易...

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被子类本身调用?

或者我做的事情完全是愚蠢的? : - )

5 个答案:

答案 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)

您应该让您的类返回一个接口,每个类(ParentClassChildClass)返回一个接口实例。您还应该覆盖GetExternalObject中的ChildClass方法,以便v-table指向正确的实现。

此外,您的代码有一个拼写错误 - 当您拨打Main时,您的obj方法引用ShowYourHand两次。我也改变了这一点,以引用objobj2。以下是如何使用界面实现此功能(并修复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类派生