【问题标题】:Can a base-class method return this, even in a derived class?即使在派生类中,基类方法也可以返回这个吗?
【发布时间】:2011-12-09 12:58:33
【问题描述】:

我希望能够在 C# 基类中有一个方法,可在多个派生类的对象上调用,返回对象本身,并让 CLR 知道对象的真正类型 - ie ,适当的派生类型。 有人可以建议一种方法吗? 当然,除了返回类型协方差之外,C# 没有。

类似这样,除了Method()的返回类型应该是派生类的类型,而不是基类:

public abstract class Base { 
    public Base Method() { return this; }
}

public class Derived1: Base { ... }

public class Derived2: Base { ... }

public class Main {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
    }
}

我只能想到两种方法来完成这项工作,而且我不喜欢其中任何一种:

  1. 将 Method() 的结果转换为预期的类型(例如Derived1 x = (Derived) d1.Method();)。但是强制转换是魔鬼的工具,此外,该方法的目的是返回 Derived1Derived2 或 ...,而不是 Base

  2. 在基类中将Method() 声明为抽象,并在每个派生类中分别实现。但这与分解出常用方法的想法完全背道而驰。除了返回类型之外,Method() 在每种情况下都是相同的。

【问题讨论】:

    标签: c# derived-class


    【解决方案1】:

    我相信你可以在 C# 4.0 上使用dynamic 关键字:

    public abstract class Base { 
        public dynamic Method() { return this; }
    }
    
    public class Derived1: Base { ... }
    
    public class Derived2: Base { ... }
    
    public class Main {
        public static int Main() {
            Derived1 d1 = new Derived1();
            Derived1 x = d1.Method();
            Console.WriteLine(x.GetType()); // outputs Derived1
            Derived2 d2 = new Derived2();
            Derived2 y = d2.Method();
            Console.WriteLine(y.GetType()); // outputs Derived2
        }
    }
    

    【讨论】:

    • +1,符合 OP 的要求,(替代他想到但不喜欢的 2 种方法。)
    • 我希望有问题的代码不是 .NET 3.5。不过,您会获得最佳答案奖,因为这正是我想要的。
    • dynamic 是比施法更糟糕的魔鬼工具。在这种特殊情况下,编译器和运行时会合作为您插入强制转换。
    • 工作出色。 @AntonTykhyy 我同意和不同意你。在这种情况下,public dynamic Method() 是一种气味。如果Method() 受到保护,那将解决这种气味。最清楚的例子是流畅链接,其中基类使用 return this,这会破坏链接或需要在派生类型中进行强制转换,除非它是动态的。
    • @ChrisMarisic "为什么你想要这个的最清楚的例子是流畅的链接,基类使用 return this" 这正是我当时想要做的.
    【解决方案2】:

    好的,我误读了这个问题。我认为 OP 确实 想要覆盖该方法。显然不是,所以泛型是前进的方向:

    public abstract class Base<T> where T : Base<T>
    {
        public T Method()
        {
            return (T) (object) this;
        }
    }
    
    public class Derived1 : Base<Derived1>
    {
    }
    

    仍有演员阵容,但不幸的是,据我所知,这是不可避免的。您几乎肯定也想在构造函数中检查它:

    public Base()
    {
        if (this as T == null)
        {
            // throw some exception
        }
    }
    

    它很丑,但它会起作用......而且丑陋仅限于基类。


    原答案

    一种方法是将Method 放入通用接口并显式实现它:

    public interface IFoo<T> {
        T Method();
    }
    
    public abstract class Base : IFoo<Base>
    {
        Base IFoo<Base>.Method()
        {
            return this;
        }
    }
    
    public class Derived1 : IFoo<Derived1>
    {
        public Derived1 Method()
        {
            // If you need to call the base version, you'll
            // need ((IFoo<Base>)this).Method()
            return this;
        }
    }
    

    这不是很好,但它会工作......在可能的情况下,我想我会尽量避免需要它,老实说。 (是的,我在实现协议缓冲区时遇到过类似的情况。这很烦人。)

    【讨论】:

    • 该死的@jonskeet,;) 我也建议使用通用方法...你更快;)
    • 这不是罗斯列为不喜欢的选项 2,因为您必须明确实施它吗?
    • @Mr.Mindor:有点——我没有注意到派生类不想覆盖该方法。那么有更好的方法:)
    • @Jon Skeet 也许这属于一个单独的问题,但我真的很好奇。与扩展方法 public static T Method&lt;T&gt;(this T derv) {return derv;} 相比,以这种方式使用泛型(修改后的答案)是否有好处?可能在项目的其他地方)还有什么?
    • @Mr.Mindor:大概是对 T 和它里面的相关方法体有一个约束?有趣的。是的,我喜欢它。将不得不考虑是否有缺点......
    【解决方案3】:

    似乎有很多方法可以做到这一点: 也可以使用扩展方法。
    这样做的好处是允许您处理您不拥有的类,并且您只需执行一次,而不是为每个派生/基类执行一次。 这将完全符合您的要求。

    public class BaseClass
    {
    
    }
    public class DerivedClass: BaseClass
    {
    
    }
    public static class BaseClassHelpers
    {
        public static T Method<T>(this T b) where T : BaseClass
        {
            return b;
        }
    }    
    

    使用中:

    DerivedClass d = new DerivedClass();
    DerivedClass dd = d.Method();
    Console.WriteLine(dd.GetType());
    

    控制台结果:

    DerivedClass
    

    【讨论】:

    • 打印出dd.GetType() 并不能真正证明这是对象的执行时类型。如果您将dd 声明为DerivedClass 类型(当然应该可以),您的示例会更有说服力。
    • 这是我的首选解决方案,因为它“最安全”——没有动态关键字,也没有强制转换,因此它在运行时永远不会失败。
    【解决方案4】:

    也有泛型但没有接口:

    public abstract class Base<T> where T : Base<T>
    {
        public virtual T Method()
        {
            return (T) this;
        }
    }
    
    public class Derived1 : Base<Derived1>
    {
        public override Derived1 Method()
        {
            return base.Method();
        }
    }
    
    public class Derived2: Base<Derived2> { }
    
    public class Program {
    public static int Main() {
        Derived1 d1 = new Derived1();
        Derived1 x = d1.Method();
        Derived2 d2 = new Derived2();
        Derived2 y = d2.Method();
        return 0;
    }
    

    【讨论】:

    • 这本质上也是 OP 不喜欢的选项 2:需要在每个派生类中编写 Method()。
    • @Mr.Mindor 不,不需要在每个派生类中重写 Method()(它无论如何都是虚拟的)请参阅 Derived2。我只在 Derived1 中提供了一个实现,以表明返回类型确实是 Derived1。
    • 这是我将要使用的,因为我目前受限于 .NET 3.5 解决方案。但是当我们跳到 4.0 时,我会改用 smoak 的。但是谢谢你,nemesv!
    【解决方案5】:

    如果您的方法具有 T 类型的参数,则不需要显式泛型参数。

    public class Base
    {
        public T Method<T>()where T: Base
        {
            return (T)this;
        }
    }
    
    public class Derived1 : Base { }
    public class Derived2 : Base { }
    
    Derived1 d1 = new Derived1();
    Derived1 x = d1.Method<Derived1>();
    Derived2 d2 = new Derived2();
    Derived2 y = d2.Method<Derived2>();
    

    【讨论】:

      【解决方案6】:

      这是一个没有强制转换的解决方案

      public abstract class Base<T> where T : Base<T>
      {
          public T Method()
          {
              return ThisDerived;
          }
      
          // It is worth to do this if you need the "this" of the derived class often in the base class
          protected abstract T ThisDerived { get; }
      }
      
      public class Derived1 : Base<Derived1>
      {
          protected override Derived1 ThisDerived{ get { return this; } } 
      }
      
      public class Derived2 : Base<Derived2>
      {
          protected override Derived2 ThisDerived { get { return this; } }
      }
      

      【讨论】:

        【解决方案7】:

        使用new 关键字和隐藏签名有一种非常简单的方法可以实现所需的结果:

        class Base
        {
            public virtual Base Method () { return this ; }
        }
        
        class Derived1: Base 
        { 
            public new Derived1 Method () { return this ; }
        }
        
        class Derived2: Base
        {
            public new Derived2 Method () { return this ; }
        }
        

        【讨论】:

          猜你喜欢
          • 2014-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-12
          • 1970-01-01
          相关资源
          最近更新 更多