【问题标题】:How to Inherit method but with different return type?如何继承方法但返回类型不同?
【发布时间】:2013-11-06 10:27:36
【问题描述】:

给定以下类:

ClassA
{
     public ClassA DoSomethingAndReturnNewObject()
     {}    
}

ClassB : ClassA
{}

ClassC : ClassA
{}

有没有办法让ClassBClassC继承方法但自定义返回类型到自己的类?

我不想从 ClassA 复制方法并在那里更改类型。

当我调用ClassB.DoSomethingAndReturnNewObject() 时,我需要得到一个ClassB 对象。

当我调用ClassC.DoSomethingAndReturnNewObject() 时,我需要得到一个ClassC 对象。

类似于调用基于当前类型的构造函数,例如:this.GetType()?但我不知道如何真正做到这一点。

【问题讨论】:

    标签: c# inheritance


    【解决方案1】:

    不,您提到的功能称为return type covariance。 C# 不支持它。

    【讨论】:

    • 虽然我认为它会在 c# 4.0 中没有?
    • jk:不。我不认为 C# 会支持这个特性。 C# 4.0 支持泛型的安全协变/逆变
    • 谷歌搜索该术语也会在 Microsoft Connect 上产生此建议:connect.microsoft.com/VisualStudio/feedback/…
    • 正确。我们添加的是通用方差,而不是返回类型协方差。
    【解决方案2】:

    您需要创建一个受保护的虚拟方法供DoSomethingAndReturnNewObject 使用:

    class ClassA
    {
        protected virtual ClassA Create()
        {
            return new ClassA()
        }
    
        public ClassA DoSomethingAndReturnNewObject()
        {
            ClassA result = Create();
            // Do stuff to result
            return result;
        }
    }
    
    class ClassB : ClassA
    {
         protected override ClassA Create() { return new ClassB(); }
    }
    
    class ClassC : ClassA
    {
         protected override ClassA Create() { return new ClassC(); }
    }
    

    请注意,返回类型仍然是 ClassA,但对象实例类型将是特定的类。

    【讨论】:

    • 好主意,我喜欢这个。
    【解决方案3】:

    您所描述的是 covariant return type,并且在 C# 中不受支持。

    但是,您可以将 ClassA 创建为开放泛型,并让封闭的泛型继承者返回它们自己的类型。

    例子:

    public abstract class ClassA<T> where T: ClassA<T>, new()
    {
        public abstract T DoSomethingAndReturnNewObject();
    }
    
    public class ClassB: ClassA<ClassB>
    {
        public override ClassB DoSomethingAndReturnNewObject()
        {
            //do whatever
        }
    }
    

    【讨论】:

    • “ClassB 和 ClassC 不能从 ClassA 继承并重新定义返回类型,同时仍然支持多态性” - 我不确定你的意思;如果它是一个通用的陈述,那么它是错误的。他要求的是协变返回类型。 C++ 和 Java 都支持这一点,而且很明显不会“破坏多态性”——如果这是暗示的话,它是完全类型安全的。这只是 CLR 的一个限制。
    • 您确实需要在通用约束中使用new,并且该类必须支持默认构造函数。我从 DoSomethingAndReturn__NewObject__() 的名字猜到了这一点
    • @Anthony 哦,很好,我并没有真正注意方法的名称。我在想 OP 会修改当前实例并返回它;
    • @Pavel 只要遵循 Liskov 替换原则,它就可以工作,这是真的。我会修改我的答案以反映。
    【解决方案4】:
    class ClassA<T> where T : ClassA<T>, new()
    {
        public T DoSomethingAndReturnNewObject()
        {
            return new T();
        }
    }
    
    class ClassB : ClassA<ClassB> { }
    
    class ClassC : ClassA<ClassC> { }
    

    测试:

    ClassB b1 = new ClassB();
    
    ClassB b2 = b1.DoSomethingAndReturnNewObject(); // returns instance of ClassB
    

    【讨论】:

      【解决方案5】:

      嗯,正确的答案是否定的,一般来说,这是一个坏主意。如果您要返回完全不同的东西,请另谋出路。

      但是,如果您没有返回完全不同的东西,界面可以解决您的问题。与其返回一个类,不如返回一个接口,并让 A、B 和 C 类返回以他们认为合适的方式实现该接口的对象。

      【讨论】:

      • 返回类型不是“完全不同的东西”。 ClassB 和 ClassA 之间存在“is-a”关系。声明原始返回值的更具体的返回类型并不违反里氏替换原则;事实上,java 从 java 1.5 开始就允许它。对于输入参数,情况正好相反:在派生类中应该允许声明更通用的输入参数。
      • 是的,但最终的问题是为什么需要关心它是 B 级还是 C 级?如果你需要一些具体的东西,那么他的观点是正确的。
      • wcoenen,我不确定我是否理解您的问题。当前流行的答案是一个很好的解决方案。如果您说子类实际上应该具有更具体的返回值(不支持,所以我不知道我们为什么要讨论这个),那么我不同意。这是一种糟糕的形式。
      【解决方案6】:

      这不是继承,因为方法的返回类型是其签名的一部分。您没有改变方法,而是创建了一个全新的方法。

      您确实有一些选择。例如,您可以将方法 DoSomethingAndReturnNewObject 设为基于泛型的方法,并让它返回其泛型类型。这可能是您正在寻找的确切行为的最直接路径。

      另一种选择是保留方法签名,让子类方法返回ClassBClassC 的实例。然后客户端代码需要负责转换,以便将这些对象用作其适当的派生类。

      ClassA 的公共接口不够用有什么原因吗?如果您正确地派生和覆盖了虚拟成员,多态性将为您提供正确的功能,如果您只使用 ClassA 成员。

      【讨论】:

        【解决方案7】:

        也许泛型将成为您的救星。看看这个链接: C# Generics Part 3/4: Casting, Inheritance, and Generic Methods

        bstract class  B<T> {
           public abstract T Fct(T t);
        }
        class D1 : B<string>{
           public override string Fct( string t ) { return "hello"; }
        }
        class D2<T> : B<T>{
           public override T Fct(T t) { return default (T); }
        }
        

        【讨论】:

          【解决方案8】:

          我最终设计了以下解决方案。就我而言,它已经足够有用了,但它假设 ClassA 知道它的派生词并且仅限于这两个选项。不是真正的高层次思维,但它确实有效。 :)

          ClassA
          {
              public ClassA DoSomethingAndReturnNewObject()
              {
                  if (this.GetType() == typeOf(ClassB))
                  {
                      return new ClassB(values);
                  }
                  else
                  {
                      return new ClassC(values):
                  }
              }    
          }
          
          ClassB : ClassA
          {}
          
          ClassC : ClassA
          {}
          

          【讨论】:

            【解决方案9】:

            我认为您需要使用新的返回类型创建一个新函数并在另一个函数内部调用,并操作返回的类型。 否则就不合逻辑了! (带有继承的定义/思想)

            【讨论】:

              【解决方案10】:

              有一个非常简单的答案:使所有方法返回类型为“对象”。您显然返回了不同的类型、字符串和整数以及诸如此类的东西,并且它们在到达目的地后将保持原样。但是编译器非常高兴——一切都是“对象”。您确实放弃了编译时类型检查,这不是一件好事,但每隔一段时间,对于一些深度 OO 编程来说,回报是值得的。

              【讨论】:

                猜你喜欢
                • 2017-10-21
                • 1970-01-01
                • 2017-03-26
                • 1970-01-01
                • 2012-05-08
                • 1970-01-01
                • 2020-11-20
                • 2015-05-22
                • 1970-01-01
                相关资源
                最近更新 更多