Overriding fields or properties in subclasses
我有一个抽象基类,我想声明一个字段或属性,该字段或属性将在每个继承自父类的类中具有不同的值。
我想在基类中定义它,这样我就可以在基类方法中引用它——例如,重写ToString以说"这个对象的类型是属性/字段"。我有三种方法可以做到这一点,但我想知道——什么是最好的或公认的方法?新手问题,对不起。
选项1:使用抽象属性并在继承的类上重写它。这得益于强制执行(你必须覆盖它),而且它是干净的。但是,返回一个硬代码值而不是封装一个字段感觉有点错误,它只是几行代码而不是简单的代码。我还必须为"set"声明一个身体,但这并不重要(可能有一种方法可以避免我不知道的事情)。
1 2 3 4 5 6 7 8 9 10 11 12 13 | abstract class Father { abstract public int MyInt { get; set;} } class Son : Father { public override int MyInt { get { return 1; } set { } } } |
选项2我可以声明一个公共字段(或一个受保护的字段),并在继承的类中显式重写它。下面的示例将警告我使用"new",我可能会这样做,但感觉不对,它打破了多态性,这就是关键所在。似乎不是个好主意…
1 2 3 4 5 6 7 8 9 | abstract class Mother { public int MyInt = 0; } class Daughter : Mother { public int MyInt = 1; } |
选项3我可以使用一个受保护的字段并在构造函数中设置值。这看起来很整洁,但我要确保构造函数总是设置这个值,并且对于多个重载的构造函数,总是有可能某些代码路径不会设置该值。
1 2 3 4 5 6 7 8 9 10 11 12 | abstract class Aunt { protected int MyInt; } class Niece : Aunt { public Niece() { MyInt = 1; } } |
这是一个理论上的问题,我想答案必须是选项1,因为它是唯一安全的选项,但我只是想和C打交道,并想问更多经验的人这个问题。
在这三种解决方案中,只有选项1是多态的。
不能重写字段本身。这就是为什么选项2返回新的关键字警告。
警告的解决方案不是附加"new"关键字,而是实现选项1。
如果你需要你的字段是多态的,你需要把它包装在一个属性中。
如果您不需要多态行为,选项3是可以的。不过,您应该记住,在运行时访问属性myint时,派生类对返回的值没有控制权。基类本身能够返回这个值。
这就是您的属性的真正的多态实现的外观,允许派生类处于控制之中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | abstract class Parent { abstract public int MyInt { get; } } class Father : Parent { public override int MyInt { get { /* Apply formula"X" and return a value */ } } } class Mother : Parent { public override int MyInt { get { /* Apply formula"Y" and return a value */ } } } |
选项2是非启动项-您不能覆盖字段,只能隐藏它们。
就我个人而言,我每次都会选择1。我试着随时保持田地的私密。当然,如果你真的需要重写这个属性的话。另一个选项是在从构造函数参数设置的基类中具有只读属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | abstract class Mother { private readonly int myInt; public int MyInt { get { return myInt; } } protected Mother(int myInt) { this.myInt = myInt; } } class Daughter : Mother { public Daughter() : base(1) { } } |
如果值在实例的生命周期中不发生变化,那么这可能是最合适的方法。
选项2是个坏主意。它会产生一种叫做阴影的东西;基本上你有两个不同的"我的"成员,一个在母亲身上,另一个在女儿身上。问题在于,在母亲中实现的方法将引用母亲的"myint",而在女儿中实现的方法将引用女儿的"myint"。这可能会导致一些严重的可读性问题,并在后期造成混乱。
就我个人而言,我认为最好的选择是3;因为它提供了一个清晰的集中值,并且儿童可以在内部引用它,而不必费心定义自己的字段——这是选项1的问题。
你可以这样定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 | abstract class Father { //Do you need it public? protected readonly int MyInt; } class Son : Father { public Son() { MyInt = 1; } } |
通过将该值设置为只读,它可以确保该类的值在对象的生命周期内保持不变。
我想下一个问题是:你为什么需要它?
你可以这样做
1 2 3 4 5 6 7 8 9 10 11 | class x { private int _myInt; internal virtual int myInt { get { return _myInt; } set { _myInt = value; } } } class y : x { private int _myYInt; public override int myInt { get { return _myYInt; } set { _myYInt = value; } } } |
虚拟允许您获得一个属性,一个执行某些操作的主体,并且仍然允许子类覆盖它。
如果您正在构建一个类,并且希望该属性有一个基值,那么请在基类中使用virtual关键字。这允许您选择性地重写属性。
使用上面的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //you may want to also use interfaces. interface IFather { int MyInt { get; set; } } public class Father : IFather { //defaulting the value of this property to 1 private int myInt = 1; public virtual int MyInt { get { return myInt; } set { myInt = value; } } } public class Son : Father { public override int MyInt { get { //demonstrating that you can access base.properties //this will return 1 from the base class int baseInt = base.MyInt; //add 1 and return new value return baseInt + 1; } set { //sets the value of the property base.MyInt = value; } } } |
在一个程序中:
1 2 |
我做到了…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace Core.Text.Menus { public abstract class AbstractBaseClass { public string SELECT_MODEL; public string BROWSE_RECORDS; public string SETUP; } } namespace Core.Text.Menus { public class English : AbstractBaseClass { public English() { base.SELECT_MODEL ="Select Model"; base.BROWSE_RECORDS ="Browse Measurements"; base.SETUP ="Setup Instrument"; } } } |
这样您仍然可以使用字段。
如果修改抽象基类以要求构造函数中的属性值,则可以使用选项3,这样不会丢失任何路径。我真的会考虑这个选择。
1 2 3 4 5 6 7 8 9 | abstract class Aunt { protected int MyInt; protected Aunt(int myInt) { MyInt = myInt; } } |
当然,您仍然可以选择将字段设置为私有,然后根据需要公开受保护或公共财产获取者。
我将使用选项3,但是有一个抽象的setmyint方法,子类被强制实现。这样就不会出现派生类忘记在构造函数中设置它的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 | abstract class Base { protected int myInt; protected abstract void setMyInt(); } class Derived : Base { override protected void setMyInt() { myInt = 3; } } |
顺便说一句,对于选项1,如果不指定set;在抽象基类属性中,派生类将不必实现它。
1 2 3 4 5 6 7 8 9 10 11 12 | abstract class Father { abstract public int MyInt { get; } } class Son : Father { public override int MyInt { get { return 1; } } } |