如何限制层次结构中的覆盖?

发布于 2024-09-11 18:46:13 字数 1054 浏览 6 评论 0原文

这是一个例子,我只是好奇它是如何实现的。

我只想让 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";
    }
}

这将如何实现?

我知道我可以通过密封父类中函数的实现来停止子类中的重写。我尝试使用 Legs 属性进行此操作,但无法使其正常工作。

谢谢

This is an example, I'm just curious as to how it would be achieved.

I want to enable only subclasses of Animal to be able to set the number of legs that they have, but I still want them to be able to set their own colour. Therefore, I want to restrict classes further down the hierarchy from then altering this Legs property.

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!";
    }
}

For example, I want to eliminate this kind of subclass:

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

How would this be achieved?

I'm aware that I could stop overriding in a subclass by sealing the implementation of a function in the parent class. I tried this with the Legs property but I couldn't get it to work.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

十年不长 2024-09-18 18:46:13

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

我将从 Animal 中删除 Legs 属性的设置器,因为这是一个实现细节。相反,只需有一个抽象的 getter。然后,子类可以决定如何最好地实现这一点,或者通过返回硬编码值,或者通过使用字段来存储该值。

In part, this goes against OO principles. Your superclass Animal makes available a contract which includes the set/get for Legs. You then want a subclass to be able to restrict that interface to disallow set Legs. Since subclassing provides an "is-a" relationship, restricting the interface goes against this, which would mean that subclasses would not be true subtypes, since the set Legs methods is not present.

I would remove the setter for the Legs property from Animal, since that is an implementation detail. Instead simply have an abstract getter. Subclasses can then decide how best to implement this, either by returning a hard-coded value or by using a field to store the value.

少钕鈤記 2024-09-18 18:46:13

您不应将 Legs 作为抽象类的字段,而应仅将其设为属性(删除 setter),并使其抽象。

在动物中

public abstract int Legs { get; }

在狗中

public override sealed int Legs { get { return 4; } }

Rather than having the Legs a field of the abstract class, you should make it a property only (remove the setter), and make it abstract.

In Animal

public abstract int Legs { get; }

In Dog

public override sealed int Legs { get { return 4; } }
违心° 2024-09-18 18:46:13

在 Java 中,您可以将 getter 和 setter 设置为最终方法,这样它们就不会被重写。在C#中,我相信你想要的关键字是“sealed”;您可以密封该方法,但不是整个子类。

您可以将变量本身设置为私有,因此子类必须使用 getter/setter 来访问该变量。

In Java, you'd make the getters and setters final methods, so they couldn't be overridden. In C#, I believe the keyword you want is "sealed"; you'd seal the method, but not the entire subclass.

You'd make the variable itself private, so subclasses would have to use a getter/setter to access the variable.

流年里的时光 2024-09-18 18:46:13
class Quadruped : Animal
{
    public int Legs { get {return 4;} }
}

class Dog : Quadruped
{
    ...
}

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

我认为如果你遇到这类问题,你需要重新安排层次结构。

class Quadruped : Animal
{
    public int Legs { get {return 4;} }
}

class Dog : Quadruped
{
    ...
}

?

I guess then you'd never want to classify an octopus as a quadruped.

I think that if you have this sort of problem, you need to re-arrange the hierarchy.

秉烛思 2024-09-18 18:46:13

您的问题意味着这些类代表理想动物,而不是实际的动物实例 - 毕竟,个体动物的腿数量是可变的。

如果是这种情况,那么您一开始就不需要 Legs 的 setter。 setter 是一个设计错误,因为语义是错误的:没有调用者,包括子类,应该能够在任意时间设置腿的数量。

相反,要求受保护的 Animal 构造函数中的腿数(可能还有颜色):

public abstract class Animal {
    protected Animal(int legs) {
        this.legs = legs;
    }
}

public class Dog: Animal {     
    public Dog(): base(4) {}
}

如果您稍后决定 Dog 子类需要能够设置此值,您只需添加一个允许它的新构造函数即可。

Your question implies that these classes represent ideal animals rather than actual animal instances - after all, individual animals will have a variable number of legs.

If that's the case, you don't really want a setter for Legs in the first place. The setter is a design error, since the semantics are wrong: no caller, including subclasses, should be able to set the number of legs at an arbitrary time.

Instead, demand the number of legs in a protected Animal constructor (and probably colour as well):

public abstract class Animal {
    protected Animal(int legs) {
        this.legs = legs;
    }
}

public class Dog: Animal {     
    public Dog(): base(4) {}
}

If you later decide that Dog subclasses need to be able set this after all, you can just add a new constructor that allows it.

七度光 2024-09-18 18:46:13
//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;
    }
}
//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;
    }
}
国粹 2024-09-18 18:46:13

基本上,这不能使用 C# 来完成,正如本线程中的几篇文章所述。有人认为这不是正确的面向对象,但我不同意。为了便于参考,如果您手头有的话,请查看 OOSC。这里使用的术语是“不变继承”。给出的示例是一个矩形(始终是四个边),继承自Polygon(任意数量大于2的边)。

规则很简单,引用:

类的不变属性是
断言的布尔值 and
出现在 it 不变子句中并且
其不变性质
父母,如果有的话。

Bertrand Meyer 使用合同设计。在较小程度上,这也适用于 C#。使用 .NET 4.0,已通过 Spec# 提供。

关于这不是正确的面向对象的论点:该论点是正确的(防止链上的继承违背了契约),但不阻止继承,而是通过使用不变子句对值添加限制,OO范例被保存并且继承链保持完整。 的事情:

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
}

像这样

编辑(密封解决方案,后续

按照其中一个线程的要求,这里有一个小示例,用于密封成员的进一步继承(这里许多人认为违反了 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
}

Basically, this cannot be done using C#, as has been said by several posts in this thread. It has been argued that this is not proper OO, but I beg to disagree. For ease of reference, if you have it handy, check page 464+ of OOSC. The term to be used here is Invariant inheritance. The example given is a Rectangle (always four sides), inheriting from Polygon (any amount of sides larger then 2).

The rule is simple, quote:

The invariant property of a class is
the boolean and of the assertions
appearing in it invariant clause and
of the invariant properties of its
parents, if any.

Bertrand Meyer uses Design By Contract. To a lesser extend this is available to C# as well. With .NET 4.0, it has become available through Spec#.

About the argument that this is not proper OO: the argument is correct (preventing inheritance down the chain defies the contract), but without preventing inheritance, but adding restrictions on the values by using invariant clauses, the OO paradigm is saved and the inheritance chain remains intact. Something like this:

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
}

 

Edit (solution with sealed, a follow-up on this)

As requested in one of the threads, here's a little example that works with sealing off the further inheriting of a member (something many here considered a breach of OO, while the language designers clearly understood that it isn't):

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
}
嘴硬脾气大 2024-09-18 18:46:13

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

例如。

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

如果 Animal.Legs 是公共财产,我有充分的理由相信这会起作用。但如果打电话的人送来一只狗怎么办?

You might also think about what would happen if someone created a variable or parameter or type Animal and then tried to set its Legs property. Would you throw a specific exception if it was one of the subclasses that doesn't allow its legs to be set?

For example.

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

If Animal.Legs is a public property, I have every reason to believe this will work. But what if the caller passes in a dog?

满地尘埃落定 2024-09-18 18:46:13

在 Animal 的子类中,将 Leg 属性的 set 访问器设为私有。
[注意:在动物类中,Leg 属性必须设为虚拟。]

    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";
        }
    }

这将阻止 Dog 的任何派生物设置 Leg 属性。

In the subclasses of Animal, make the set accessor of Leg property a private.
[Note: In Animal Class Leg property has to be made virtual.]

    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";
        }
    }

This will stop any derivative of Dog from setting Leg property.

栀梦 2024-09-18 18:46:13

如前所述,正确的方法是将腿数设置为抽象的只读属性。为了使狗的腿计数不能被覆盖,我认为有必要创建一个中间级类,其目的是定义 NumberOfLegs 属性的实现,然后使用公共 NumberOfLegs 函数定义 Dog 类,该函数隐藏基类类财产。任何覆盖腿数的努力都将针对影子属性,因此原始属性不能再被覆盖。

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

The proper approach as noted is to make the number of legs an abstract read-only property. To make dogs whose leg count can't be overridden, I think it's necessary to create an intermediate-level class whose purpose is to define an implementation for the NumberOfLegs property, and then define the Dog class with a public NumberOfLegs function that shadows the base class property. Any effort to override the number of legs would be directed at the shadow property, so the original could no longer be overridden.

It would be nicer if one could define distinct Overrides and Shadows implementations for the same property in the same scope (with the Overrides one only being used when the property is accessed in the base class) but I don't know any way to do that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文