【问题标题】:Overriding method to change return type更改返回类型的重写方法
【发布时间】:2011-11-09 02:39:27
【问题描述】:

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

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

还是我在做一些完全愚蠢的事情? :-)

【问题讨论】:

标签: c#


【解决方案1】:

还是我在做一些完全愚蠢的事情? :-)

是的,你是。您不能通过覆盖来更改方法的返回类型。无论如何,我在您的示例中不明白。只需保持返回类型不变并返回一个新的ExternalObjectStub。这行得通,因为ExternalObjectStub 派生自ExternalObject

通过隐藏带有new 的基本成员来更改返回类型通常是一个非常糟糕的主意,因为它会导致类不能以多态方式使用。这正是您在这里遇到的情况:如果保存引用的变量的类型是 ParentClass 类型,它会调用 ParentClass 中的方法,即使实例确实是 ChildClass 类型,因为 ChildClass不提供 GetExternalObject 的覆盖实现。

【讨论】:

    【解决方案2】:

    您使用的多态性是不正确的。您需要在子类中创建一个隐藏基类实现的新方法,并使用新的返回类型。您不能像现在这样使用虚拟方法来重载方法。

    虚拟方法用于在子类中创建不同的实现方法,而不是像您尝试做的那样“重载”它。

    方法的重载是通过改变参数而不是返回类型来完成的。

    所以要么在子类中隐藏父方法,要么创建一个具有其他名称的方法。对此使用 virtual 是行不通的。

    【讨论】:

      【解决方案3】:

      我不认为你在做任何愚蠢的事情。我发现自己经常寻找实现相同模式的方法(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();
          }
      }
      

      【讨论】:

        【解决方案4】:

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

        另外,您的代码有一个错字——当您调用ShowYourHand 时,您的Main 方法引用了obj 两次。我也将其更改为引用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";
            }
        }
        

        【讨论】:

          【解决方案5】:

          如果您想在子类中返回 ExternalObjectStub,则此 ExternalObjectStub 应派生自 ExternalObject 类

          【讨论】:

            猜你喜欢
            • 2021-11-16
            • 1970-01-01
            • 2019-11-30
            • 1970-01-01
            • 2017-03-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多