【问题标题】:Hide Public Properties of Base Class in Derived Class在派生类中隐藏基类的公共属性
【发布时间】:2013-06-03 18:53:23
【问题描述】:

我想知道我们是否可以在Derived Class 中隐藏Base Classpublic 属性。

我有以下示例问题语句用于计算不同形状的Area -

abstract class Shape
{
    public abstract float Area();
}

class Circle : Shape
{
    private const float PI = 3.14f;

    public float Radius { get; set; }
    public float Diameter { get { return this.Radius * 2; } }

    public Circle() { }

    public Circle(float radius)
    {
        this.Radius = radius;
    }

    public override float Area()
    {
        return PI * this.Radius * this.Radius;
    }
}

class Triangle : Shape
{
    public float Base { get; set; }
    public float Height { get; set; }

    public Triangle() { }

    public Triangle(float @base, float height)
    {
        this.Base = @base;
        this.Height = height;
    }

    public override float Area()
    {
        return 0.5f * this.Base * this.Height;
    }
}

class Rectangle : Shape
{
    public float Height { get; set; }
    public float Width { get; set; }

    public Rectangle() { }

    public Rectangle(float height, float width)
    {
        this.Height = height;
        this.Width = width;
    }

    public override float Area()
    {
        return Height * Width;
    }
}

class Square : Rectangle
{
    public float _side;

    public float Side
    {
        get { return _side; }
        private set
        {
            _side = value;
            this.Height = value;
            this.Width = value;
        }
    }

    // These properties are no more required
    // so, trying to hide them using new keyword
    private new float Height { get; set; }
    private new float Width { get; set; }

    public Square() : base() { }

    public Square(float side)
        : base(side, side)
    {
        this.Side = side;
    }
}

现在有趣的部分是在Square 类中HeightWidth 属性不再需要(因为它被Side 属性替换)来暴露于外部世界所以我使用new 关键字隐藏它们。但它不起作用,用户现在可以设置 HeightWidth -

class Program
{
    static void Main(string[] args)
    {
        Shape s = null;

        // Height & Width properties are still accessible :(
        s = new Square() { Width = 1.5f, Height = 2.5f };

        Console.WriteLine("Area of shape {0}", s.Area());
    }
}

有谁知道在 C# 中可以隐藏派生类的不需要的属性吗?

重要提示: 有人可能会指出Shape -> Rectangle -> Square 不是一种合适的继承设计。但我想保持这种状态,因为我不想在Square 类中再次编写“不精确”但类似的代码(注意:Square 类使用其基类AreaArea 方法@ .在现实世界中,如果是这种类型的继承,方法逻辑可能会更复杂)

【问题讨论】:

  • 与其向我们展示您知道错误的设计示例,不如向我们询问您的实际情况?
  • 您尝试做的事情没有多大意义。如果你没有 Square 对象中的 Height 和 Width 属性,那么 Square 应该继承自 Shape,而不是 Rectangle。
  • @Oded 是的,在形状的情况下确实如此。但它会在RectangleShape 中重复计算Area 的逻辑。从理论上讲,每个人都知道 [b]SquareRectangle[/b]。从逻辑上讲,我们应该从Rectangle 继承Square。除了形状之外,我们可能会在real wold examples 中找到这种情况。那么这个共同的逻辑就会被复制。我的问题就是这个。
  • 有一个东西叫做'protected'
  • “每个人都知道 [b]Square 是一个 Rectangle[/b]”。我不同意,我认为这就是你的模型崩溃的原因。我认为更好的设计是Shape -> ConvexPolygon -> RegularConvexPolygon ->SquareShape -> ConvexPolygon -> Rectangle。这样一来,所有 RegularConvexPolygons 都可以共享 Area 计算(如果您的语言支持计算无穷大的限制,则一直到 Circle)。

标签: c# oop design-patterns inheritance


【解决方案1】:

任何子类型基本上仍然可以被视为基本类型的实例。如果有人将变量键入为基本类型,您当然不能隐藏成员。你能做的最多就是让他们在使用派生类型时真的很烦,例如:

[Obsolete, Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new float Width { get { return base.Width;} }

如果访问器(尤其是 set)使用不当,您也可以对 override 执行类似的操作,甚至可能是 throw

不过,这听起来更像是您应该更改继承模型,这样您就永远不想尝试删除成员。

【讨论】:

    【解决方案2】:

    我仍然认为Shape -> Rectangle -> Square 是所有问题的根源。继承不仅仅是代码重用。如果你有类似的代码来计算面积,你可以组合逻辑:即将面积计算的逻辑外部化到一个单独的类中,并在 Square 和 Rectangle 中重复使用。

    回到您最初的问题,您需要对子类隐藏基类中的属性这一事实是一种很大的设计味道 - 语言设计者绝对不会想要这样的功能。

    (您可能还想了解Liskov Substitution Principle

    【讨论】:

    • 根据Liskov Substitution Principle 如果S is a T 则S 必须继承自T 即T(base) -> S(derived)。在糟糕的情况下Square is a Rectangle 那为什么不Rectangle (base) -> Square (derived)
    • “正方形是矩形”在数学上是正确的。但不幸的是,同一行上的对象建模是不正确的。 LSP 声明子类应该能够替换任何使用基类的地方。以前在基类(即 Rectangle)上调用的“SetWidth()”不能在子类(即 Square)上调用,因为这会损害后者的状态。
    • @ParagMeshram 虽然正方形是矩形,但它们与矩形不同。见Is a Square a Rectangle?Circle-Ellipse Problem
    • @ParagMeshram 如果“Square 是一个矩形”,那么 Square 有一个 Width 和 Height,它们应该被暴露出来。仅仅因为您不再需要 Width 和Height 并不意味着它们应该被隐藏。您总是可以定义一个 ISquare 接口来为使用 Square 的其他类定义一组更简单的方法。
    • @mbeckish 接口继承似乎是一个不错的选择。 ISquare 是 IRectangle 而不会产生冲突的实现
    【解决方案3】:

    乍一看,我想我会将RectangleHeightWidth 声明为虚拟并在Square 类中覆盖它,而不是试图隐藏它们:

    class Square : Rectangle
    {    
        public float Side{get;set;}
    
        public override float Height { get{return Side;}; set{Side =value;} }
        public override float Width {get{return Side;}; set{Side =value;} }
    
        public Square() : base() { }
    
        public Square(float side)
        {
            this.Side = side;
        }
    }
    

    【讨论】:

    • 请注意,在此处更改 Side 实际上不会更改基本类型的宽度/高度
    • @MarcGravell 是的,我应该说我认为继承在这里不是一个好主意。我只是不知道我应该把钻石和平行四边形放在哪里。
    • @MarcGravell 你的评论让我觉得我错过了一些非常重要的事情,因为我认为不改变基类值不应该是一个真正的问题,只要我不调用 base.Height/Width在 Square 类中,只要 Rectangle 类没有引用虚拟 Width 和 Height 的具体实现(即一些 _width/_height)。您能否就此事提供一些提示? (或者如果提示不适合 cmets,我应该问一个问题)
    【解决方案4】:

    SquareRectangle 继承有什么优势? 也许你需要一个新的类:

    abstract class Square<TFigure> where TFigure : Shape
    {
      public float Side {set; get;}
    
      protected Square(TFigure stereotype)
      {
         Side = GetStereotypeSide(stereotype) 
      }
    
      public abstract float GetStereotypeSide(TFigure stereotype);
    }
    

    那么你的正方形可以如下所示

    class RectangleSquare: Square<Rectangle>
    {
      public RectangleSquare() : base (new Rectangle(){Height = 1, Width = 2})
      {
      }
    
      public override float GetStereotypeSide(Rectangle stereotype)
      {
         return stereotype.Height*stereotype.Width;
      }
    }
    

    如果您仍然需要一个通用的对象来表示 SquareShape 而不是您可以定义的

    interface IGeometry{
    }
    

    然后给两个类添加接口继承

        class Shape : IGeometry
      ....
    

    abstract class Square<TFigure> : IGeometry where TFigure : Shape
    

    【讨论】:

      【解决方案5】:

      我回应@aquaraga。如果您想隐藏基类中的公共内容,那么派生是不正确的。组合是更好的解决方案,必要时委托给另一个(基)类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-23
        • 2010-11-27
        • 2015-04-09
        • 2015-02-18
        • 2023-03-28
        相关资源
        最近更新 更多