【问题标题】:Avoiding Inheriting All Things from Base Class避免从基类继承所有东西
【发布时间】:2009-10-10 03:43:57
【问题描述】:

我在想这样的场景:

class Utopia => 将其字段和方法传递给派生类的基类。

class Watashi => 派生类,从乌托邦派生并继承一切

class Baka => 派生类,继承乌托邦的一些领域

上面有一些类型,Type Baka 应该继承一些特定的字段和方法,但是如何?如何指定只有 Baka 将继承而 Watashi 从 Utopia 继承所有内容的字段和方法。

示例代码:

class Utopia {
   public string Moshi;

   [Exclude(ClassName("Baka"))]
   public string HaveIt;
}

class Baka : Utopia 
{
 // only Moshi appears here
 base.[Moshi]
}

class Watashi : Utopia 
{
  base.[Moshi][HaveIt];
}

如果我想使用多态:

   Utopia _utopiaBaka = new Baka();
   _utop.[Moshi];

   Utopia _utopiaWatashi = new Watashi();
   _utopiaWatashi.[Moshi][HaveIt];

当然,Framework 也会检查派生类是否是其他类型的基类。

【问题讨论】:

    标签: c# class inheritance


    【解决方案1】:

    将乌托邦分为多个类别。拥有一个任何类都可以继承的类,所以 Baka 会继承它。

    然后,扩展这个 PartialUtopia 类,添加其余方法并让 Watashi 继承自该类。

    通过这种方式,您可以让类通过它们扩展的基类来选择它们需要的方法。

    【讨论】:

    • 我只是在想我们是否可以使用属性来实现这一点?
    • 不可能,属性不能改变方法/属性的编译时可见性。您可以通过反射查看它们,并确定是否允许调用该方法,或者是否应该因为它带有某个属性而拒绝它
    • @James:有一些 C# 的 AOP。 PostSharp 和 Aspect# 浮现在脑海中。
    • @Aaron - 在 Java 中,我可以拥有一个功能有限的基类,但一切都继承自。我也有接口,所以 Watashi 继承了两个接口,Baka 只继承了一个接口。然后我将我的方法和属性注入到这两个接口中,因此,任何继承该接口的东西都将获得适当的方法。
    • @Aaron - Java 不接受接口中的代码,但使用 AspectJ 我可以将代码注入接口。它使我能够绕过 Java 的一些限制,并且是一种与 OOP 相切的编程风格。
    【解决方案2】:

    这不是 OOP 中继承的工作方式。 Baka 和 Watashi 一样继承了 Utopia 的一切。

    【讨论】:

    • 我知道但为什么不呢?也许有一个属性或什么我可以指定在继承期间将排除哪些类型。
    • @Aaron:这样做会破坏多态性。当 Baka 被转换为 Utopia 类型时,Utopia 的所有方法都需要可用。
    • 因为你不会有作为主要目标或继承的多态性。
    • 嗯,其实它也应该防止它发生。当我将 Baka 转换为 Utopia Type 时,应该出现 Baka 可以继承的唯一方法、字段。
    • @Aaron:如果你这样做了,你作为乌托邦的 Baka 演员实际上将成为乌托邦的一个破碎实例,因为某些方法不起作用。
    【解决方案3】:

    这不是继承的工作方式,您必须拆分类。 如果你和你的女朋友生了一个孩子,你不能选择它只遗传蓝眼睛和肌肉发达的基因:)

    【讨论】:

      【解决方案4】:

      我建议研究 publicprivateprotected 之间的区别。听起来这就是您要找的东西。

      http://msdn.microsoft.com/en-us/library/ms173149.aspx

      【讨论】:

      • 实际上这些关键字都不适用于这种情况。他们不过滤它们,只是隐藏或允许。
      【解决方案5】:

      您要做的与面向对象编程中的继承概念完全不兼容。这就像在问“为什么蓝色是蓝色的?”如果蓝色不是蓝色,它就不是蓝色。

      当一个类 (Baka) 从另一个类 (Utopia) 继承时,这意味着(在 OOP 中)Baka 可以做 Utopia 可以做的所有事情 - OOP 语言提供依赖于此的功能,如果您能够覆盖您将导致可怕的异常.

      例如,如果我有一个将 Utopia 类型的类作为参数的方法,如下所示:

      public void TakesUtopia(Utopia utopia)
      {
          utopia.BePerfect();
      }
      

      我们也可以传入 Watashi 类型和 Baka 类型的类,因为它们都继承自 Utopia。 Baka,通过从 Utopia 继承,保证了 BePerfect() 方法的实现

      如果 Baka 的一个实例没有实现,将会发生可怕的事情。

      为了进一步实现这一点 - 考虑一下当 TakesUtopia 依赖于 Utopia 的行为时会发生什么。例如,乌托邦公开了 PriceOfHappiness 属性。

      public string TakesUtopia(Utopia utopia)
      {
           if (utopia.PriceOfHappiness() > 100)
           {
                return "Can't buy happiness';
           }
      }
      

      在编写我的 TakesUtopia 方法时,是否应该期望我编写代码来处理 utopia 中的任何方法都未实现的可能性?这些方法在被调用时会做什么?

      【讨论】:

      • 好吧,我不明白为什么它会违反 OOP 逻辑。例如,这就像基因工程师标记一些基因以避免将它们遗传给特定的孩子。它不会违背 OOP 的本质吗?
      • 而且我不相信它也会破坏 OOP 逻辑。
      • 如果我的类能够接收不实现 utopia 成员的对象,它如何能够接收 utopia 类型的对象?作为开发人员,我应该在这个世界上做什么?我是否应该编写代码来处理有人决定不完全从基类继承的情况,对于我对该基类成员的每一次调用?这将是完全行不通的。
      • 如果我不想编写与基类型相同的代码并且不想在派生类型中看到继承的字段怎么办?
      • Aaron,你对里氏替换原则缺乏基本的理解。 en.wikipedia.org/wiki/Liskov_substitution_principle 。 OOP 中的继承不像遗传继承,它更像是通过添加额外的东西进行克隆。如果 B 继承自 A,则 B IS AN A 和 B 具有 A 的所有功能、属性和特征。这就是为什么您可以将 B 分配给 A 引用而不会出错的原因,因为B 是 A 的完整超集。它不是(如遗传)部分 A 和部分 A2。
      【解决方案6】:

      你想具体做什么?如果你担心子类会覆盖给定的方法实现,你总是可以声明一个方法sealed

      class FooBase
      {
          sealed void Bar()
          {
              //base implementation
          }
      }
      
      class FooDerived
      {
          void Bar()  //poof! compilation error
          {
              //never reached
          }
      }
      

      或者,您可以将继承的子类放在程序集中,将非继承的子类放在程序集中。只需使用protected internal

      【讨论】:

      • 但是其他想要继承 Bar() 的类型呢?
      • @Aaron,我想你误解了...... Bar() 在这种情况下仍然被继承。这只是防止子类重新实现它。
      • @Dxmio:所以?我的意思是这不是我想要的 :) 我的意思是我不想重新实现什么的,我基本上不想看到我不想被继承的东西。
      • @Aaron 然后我引导你到其他答案。你想要的不遵循 OOP 模型。您需要重新考虑您的架构。
      【解决方案7】:

      有两个不同的概念:接口和继承。基本上他们是两个不同的问题。一个是“我想如何与这个对象进行交互?” (界面)。第二个是“这个对象在内部是如何工作的?” (继承)。

      在经典的 OOP 示例中,考虑一个基类:

      class Shape {
          virtual void draw();
          virtual int size();
      }
      

      这是一个界面,它定义了您如何使用不同的形状。现在考虑这些:

      class Square : Shape {
          ...
      }
      
      class Circle : Shape {
          ...
      }
      

      虽然这两个类的接口是相同的(draw() 和 size()),但实现不会共享任何共同点。圆形的绘图代码与正方形的绘图代码真的相关吗?不,它们只是共享相同的名称和期望的结果。

      我提出这个问题的原因是,如果您希望某些类继承其他类的某些成员,那么您可能会遇到将继承与接口混淆的问题。 AFAIK,C++ 不支持接口(我可能错了)。但是您可以使用“纯虚拟”类(如上面的 Shape)来伪造它。

      class Shape {
          virtual void draw();
      }
      
      class EdgyShape : Shape {
          virtual int width();
          virtual int height();
      }
      
      class RoundyShape : Shape {
          virtual int radius();
      }
      

      现在您有了具有层次结构的接口,但根本没有实现。

      class Circle : RoundyShape {
          ...
      }
      
      class Rectangle : EdgyShape {
          ...
      }
      
      class Square : Rectangle {
          ...
      }
      

      因此 Circle 和 Rectangle 彼此不共享代码,而 Rectangle 和 Square 则共享。并且所有 3 个共享相同的基本 Shape 界面,根据类型添加了一些内容。

      也许这可以阐明如何重构您的班级层次结构,以便您可以更好地拆分它们?

      【讨论】:

        猜你喜欢
        • 2012-03-25
        • 2011-04-14
        • 1970-01-01
        • 1970-01-01
        • 2012-06-12
        • 1970-01-01
        • 1970-01-01
        • 2011-12-07
        • 2021-04-26
        相关资源
        最近更新 更多