代码之家  ›  专栏  ›  技术社区  ›  fletcher

如何限制层次结构中的重写?

  •  7
  • fletcher  · 技术社区  · 14 年前

    我只想启用 Animal 能够设置他们的腿数,但我仍然希望他们能够设置自己的颜色。因此,我想进一步限制层次结构中的类,然后修改这个 Legs

    public abstract class Animal
    {
        public string Colour { get; protected set; }
        public int Legs { get; protected set; }
    
        public abstract string Speak();
    }
    
    public class Dog : Animal
    {
        public Dog()
        {
            Legs = 4;
        }
    
        public override string Speak()
        {
            return "Woof";
        }
    }
    
    public sealed class Springer : Dog
    {
        public Springer()
        {
            Colour = "Liver and White";
        }
    }
    
    public sealed class Chihuahua : Dog
    {
        public Chihuahua()
        {
            Colour = "White";
        }
    
        public override string Speak()
        {
            return "*annoying* YAP!";
        }
    }
    

    public sealed class Dalmatian : Dog
    {
        public Dalmatian()
        {
            Legs = 20;
            Colour = "Black and White";
        }
    }
    

    我知道我可以通过密封父类中函数的实现来停止子类中的重写。我试过这个了 但我没法让它工作。

    谢谢

    10 回复  |  直到 14 年前
        1
  •  10
  •   Mark H    14 年前

    与其将Legs作为抽象类的字段,不如将其仅作为属性(删除setter),并将其抽象化。

    public abstract int Legs { get; }
    

    在狗身上

    public override sealed int Legs { get { return 4; } }
    
        2
  •  11
  •   mdma    14 年前

    在某种程度上,这违背了OO原则。你的超类动物提供一个合同,其中包括设置/获取腿。然后您希望子类能够限制该接口以禁止set Legs。由于子类化提供了“is-a”关系,因此限制接口与此相反,这意味着子类将不是真正的子类型,因为set Legs方法不存在。

        3
  •  5
  •   Dean J    14 年前

    在Java中,getter和setter是最终的方法,因此它们不能被重写。在C#,我相信你想要的关键字是“sealed”;您可以密封方法,但不能密封整个子类。

        4
  •  4
  •   izb    14 年前
    class Quadruped : Animal
    {
        public int Legs { get {return 4;} }
    }
    
    class Dog : Quadruped
    {
        ...
    }
    

    ?

    我猜你永远不会想把章鱼归为四足动物。

        5
  •  4
  •   Jeff Sternal    14 年前

    你的问题意味着这些类代表

    如果是这样的话,你不会真的想要一个二传手 Legs 首先。setter是一个设计错误,因为语义是错误的:任何调用程序,包括子类,都不能在任意时间设置分支数。

    相反,要求在一个受保护的 Animal

    public abstract class Animal {
        protected Animal(int legs) {
            this.legs = legs;
        }
    }
    
    public class Dog: Animal {     
        public Dog(): base(4) {}
    }
    

    如果你以后决定 Dog 子类需要能够设置这一点毕竟,您可以添加一个新的构造函数来允许它。

        6
  •  1
  •   Arseny    14 年前
    //overload Leg property in Dog class and make set as private
    public abstract class Animal
    {
        public string Colour { get; protected set; }
        private int legs;
        public int Legs
        {
            get { return legs; }
            protected set { legs = value; }
        }
    
        //public int Legs { get; protected set; }
    
        public abstract string Speak();
    }
    public class Dog : Animal
    {
        public int Legs
        {
            get { return base.Legs; }
    
            private set { base.Legs = value; }
        }
        public Dog()
        {
            Legs = 4;
        }
    }
    
        7
  •  1
  •   Community Navdeep Singh    7 年前

    OOSC . 这里使用的术语是 不变遗传 . 给出的例子是 Rectangle (总是四面),继承自 Polygon (大于2的任何数量的边)。

    布尔值 and 关于断言 它的不变性质 父母,如果有的话。

    has become available through Spec#

    关于 这不合适

    abstract class Animal
    {
        public abstract Legs { get; }
    }
    
    class Dog : Animal
    {
        public Dog { } 
    
        [InvariantMaximum(4), InvariantMinimum(4)]
        public override Legs { get { return 4; } }
    }
    
    class Labrador : Dog
    {
        public override Legs { get { return 5; } }    // compiler error
    }
    
    class Chihuahua: Dog
    {
        public override Legs { get { return 4; } }    // OK
    }
    

    编辑(解决方案已封存,后续跟进) this

    根据其中一个线程的要求,这里有一个小示例,用于阻止成员的进一步继承(这里的许多人认为这是对OO的违反,而语言设计者清楚地知道这不是):

    public abstract class Animal
    {
        public abstract int Legs {get;}
    }
    
    public class Dog : Animal
    {
        public sealed override int Legs {get { return 4; } }
    }
    
    public class Labrador : Dog
    {
        public override int Legs { get; }    // compiler error
    }
    
        8
  •  0
  •   John M Gant aman_novice    14 年前

    您还可以考虑如果有人创建了一个变量、参数或类型Animal,然后试图设置它的Legs属性,会发生什么。如果它是不允许设置其分支的子类之一,您会抛出特定的异常吗?

    例如。

    public void SpeedUpMyAnimal(Animal animal) {
        animal.Legs *= 2;
    }
    

    Animal.Legs 是公共财产,我有充分的理由相信这会奏效。但是如果打电话的人经过一只狗呢?

        9
  •  0
  •   SaravananArumugam    14 年前

    在动物的子类中,将Leg属性的set访问器设为私有。

        public abstract class Animal
        {
            public string Colour { get; protected set; }
            public virtual int Legs { get; protected set; }
    
            public abstract string Speak();
        }
    
        public class Dog : Animal
        {
            public Dog()
            {
                Legs = 4;
            }
    
            public override int Legs
            {
                get
                {
                    return base.Legs;
                }
                private set
                {
                    base.Legs = value;
                }
            }
    
            public override string Speak()
            {
                return "Woof";
            }
        }
    

    这将阻止狗的任何衍生设置腿属性。

        10
  •  0
  •   supercat    14 年前

    如果可以在同一范围内为同一属性定义不同的覆盖和阴影实现(覆盖只有在基类中访问属性时才使用),那就更好了,但我不知道有什么方法可以做到这一点。

    推荐文章