重写子类中的字段或属性

我有一个抽象基类,我想声明一个字段或属性,它在从这个父类继承的每个类中都有不同的值。

我想在基类中定义它,这样我就可以在基类方法中引用它——例如,重写 ToString 以表示“ This object is of type 物业/工地”。 我有三种方法,我可以看到这样做,但我想知道-什么是最好的或接受的方式做到这一点?新手问题,抱歉。

选择1:
使用抽象属性并在继承的类上重写它。这是强制执行的好处(您必须覆盖它) ,而且它是干净的。但是,返回一个硬代码值而不是封装一个字段感觉有点不对,而且只有几行代码而不仅仅是几行代码。我还必须为“ set”声明一个 body,但这并不那么重要(可能有一种方法可以避免那些我不知道的东西)。

abstract class Father
{
abstract public int MyInt { get; set;}
}


class Son : Father
{
public override int MyInt
{
get { return 1; }
set { }
}
}

选择二
我可以声明一个公共字段(或者一个受保护的字段) ,并在继承的类中显式地重写它。下面的例子会给我一个使用“ new”的警告,我也许可以这样做,但是感觉不对,它破坏了多态性,这就是问题的关键所在。这似乎不是个好主意。

abstract class Mother
{
public int MyInt = 0;
}


class Daughter : Mother
{
public int MyInt = 1;
}

选择三
我可以使用一个受保护的字段并在构造函数中设置值。这看起来非常整洁,但依赖于我确保构造函数总是设置这个值,并且对于多个重载的构造函数,总是存在某些代码路径不设置值的可能性。

abstract class Aunt
{
protected int MyInt;
}


class Niece : Aunt
{
public Niece()
{
MyInt = 1;
}
}

这是一个有点理论性的问题,我想答案应该是选项1,因为它是唯一的 安全选项,但我刚刚开始了解 C # ,想问问更有经验的人。

227878 次浏览

我会选择选项3,但是有一个抽象的 setMyInt 方法,子类必须实现这个方法。这样就不会出现派生类忘记在构造函数中设置它的问题。

abstract class Base
{
protected int myInt;
protected abstract void setMyInt();
}


class Derived : Base
{
override protected void setMyInt()
{
myInt = 3;
}
}

顺便说一下,选项1中,如果没有指定 set; 在抽象基类属性中,派生类不必实现它。

abstract class Father
{
abstract public int MyInt { get; }
}


class Son : Father
{
public override int MyInt
{
get { return 1; }
}
}

如果修改抽象基类以要求构造函数中的属性值,那么可以选择选项3,这样就不会错过任何路径。我真的会考虑这个选择。

abstract class Aunt
{
protected int MyInt;
protected Aunt(int myInt)
{
MyInt = myInt;
}


}

当然,您仍然可以选择将字段设置为私有,然后根据需要公开受保护的或公共的属性 getter。

你可以这样定义:

abstract class Father
{
//Do you need it public?
protected readonly int MyInt;
}


class Son : Father
{
public Son()
{
MyInt = 1;
}
}

通过将该值设置为 readonly,可以确保该类的值在对象的生存期内保持不变。

我想下一个问题是: 你为什么需要它?

选项2是一个非起动器-你不能 重写字段,你只能 藏起来他们。

就我个人而言,我每次都会选择选项1。我一直尽量保持领域的私有性。当然,前提是你真的需要能够覆盖这个属性。另一种选择是在基类中有一个只读属性,该属性由构造函数参数设置:

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”,而在女儿中实现的方法将引用女儿的“ MyInt”。这可能会导致一些严重的可读性问题,并在以后造成混淆。

就我个人而言,我认为最好的选择是3; 因为它提供了一个明确的集中值,并且可以被孩子们在内部引用,而不必麻烦地定义他们自己的字段——这就是选项1的问题所在。

在这三种解决方案中,只有 选择一多态性

字段本身无法重写。这正是 选择二返回 新消息关键字警告的原因。

警告的解决方案不是附加“ new”关键字,而是实现选项1。

如果需要字段具有多态性,则需要将其包装在属性中。

如果不需要多态行为,选项3 是可以的。但是您应该记住,在运行时访问属性 MyInt 时,派生类对返回的值没有控制权。基类本身能够返回此值。

这就是属性的真正多态实现的外观,允许派生类位于 控制中。

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 */ }
}
}

你可以的

class x
{
private int _myInt;
public 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 允许您获得一个属性,一个执行某些操作的主体仍然允许子类覆盖它。

是我干的。

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

这样您仍然可以使用字段。

如果您正在构建一个类,并且希望该属性有一个基值,那么在基类中使用 virtual关键字。这允许您可选地重写该属性。

用你上面的例子:

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

在一个节目中:

Son son = new Son();
//son.MyInt will equal 2

当您想要有一个带有实现的抽象类时,示例实现。子类必须:

  1. 参数化抽象类的实现。
  2. 完全继承抽象类的实现;
  3. 拥有自己的实现。

在这种情况下,除了抽象类及其自己的子类之外,实现所必需的属性不应该可用。

    internal abstract class AbstractClass
{
//Properties for parameterization from concrete class
protected abstract string Param1 { get; }
protected abstract string Param2 { get; }


//Internal fields need for manage state of object
private string var1;
private string var2;


internal AbstractClass(string _var1, string _var2)
{
this.var1 = _var1;
this.var2 = _var2;
}


internal void CalcResult()
{
//The result calculation uses Param1, Param2, var1, var2;
}
}


internal class ConcreteClassFirst : AbstractClass
{
private string param1;
private string param2;
protected override string Param1 { get { return param1; } }
protected override string Param2 { get { return param2; } }


public ConcreteClassFirst(string _var1, string _var2) : base(_var1, _var2) { }


internal void CalcParams()
{
//The calculation param1 and param2
}
}


internal class ConcreteClassSecond : AbstractClass
{
private string param1;
private string param2;


protected override string Param1 { get { return param1; } }


protected override string Param2 { get { return param2; } }


public ConcreteClassSecond(string _var1, string _var2) : base(_var1, _var2) { }


internal void CalcParams()
{
//The calculation param1 and param2
}
}


static void Main(string[] args)
{
string var1_1 = "val1_1";
string var1_2 = "val1_2";


ConcreteClassFirst concreteClassFirst = new ConcreteClassFirst(var1_1, var1_2);
concreteClassFirst.CalcParams();
concreteClassFirst.CalcResult();


string var2_1 = "val2_1";
string var2_2 = "val2_2";


ConcreteClassSecond concreteClassSecond = new ConcreteClassSecond(var2_1, var2_2);
concreteClassSecond.CalcParams();
concreteClassSecond.CalcResult();


//Param1 and Param2 are not visible in main method
}