C# 重写子类中的字段或属性
我有一个抽象基类,我想声明一个字段或一个属性,该字段或属性在继承自父类的每个类中具有不同的值 我想在基类中定义它,这样我就可以在基类方法中引用它-例如,重写ToString以表示“此对象的类型为property/field”。 我有三种方法,我可以看到这样做,但我想知道-什么是最好的或被接受的方式做这件事?新手问题,对不起 选项1:C# 重写子类中的字段或属性,c#,properties,field,C#,Properties,Field,我有一个抽象基类,我想声明一个字段或一个属性,该字段或属性在继承自父类的每个类中具有不同的值 我想在基类中定义它,这样我就可以在基类方法中引用它-例如,重写ToString以表示“此对象的类型为property/field”。 我有三种方法,我可以看到这样做,但我想知道-什么是最好的或被接受的方式做这件事?新手问题,对不起 选项1: 使用抽象属性并在继承的类上重写它。这得益于强制执行(您必须覆盖它),而且它是干净的。但是,返回一个硬代码值而不是封装一个字段感觉有点错误,它只需要几行代码,而不仅仅
使用抽象属性并在继承的类上重写它。这得益于强制执行(您必须覆盖它),而且它是干净的。但是,返回一个硬代码值而不是封装一个字段感觉有点错误,它只需要几行代码,而不仅仅是。我还必须为“set”声明一个body,但这不太重要(可能有一种方法可以避免我不知道的事情) 选项2
我可以声明公共字段(或受保护字段)并在继承的类中显式重写它。下面的例子将给我一个使用“new”的警告,我可能可以这样做,但它感觉不对,它打破了多态性,这就是重点。这似乎不是个好主意
abstract class Mother
{
public int MyInt = 0;
}
class Daughter : Mother
{
public int MyInt = 1;
}
选项3我可以使用受保护的字段并在构造函数中设置值。这看起来很整洁,但依赖于我确保构造函数总是设置此值,并且对于多个重载构造函数,总是有可能某些代码路径不会设置值
abstract class Aunt
{
protected int MyInt;
}
class Niece : Aunt
{
public Niece()
{
MyInt = 1;
}
}
这是一个有点理论性的问题,我想答案应该是选项1,因为它是唯一安全的选项,但我刚刚开始接触C,我想问一下有更多经验的人。我会选择选项3,但有一个抽象的setMyInt方法,子类被迫实现。这样,您就不会有派生类忘记在构造函数中设置它的问题
abstract class Base
{
protected int myInt;
protected abstract void setMyInt();
}
class Derived : Base
{
override protected void setMyInt()
{
myInt = 3;
}
}
顺便说一句,如果您不指定set,请使用选项1;在抽象基类属性中,派生类不必实现它
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的问题。三种解决方案中只有选项1是多态的 字段本身不能被重写。这正是选项2返回关键字警告的原因 警告的解决方案不是附加“new”关键字,而是实现选项1 如果需要字段具有多态性,则需要将其包装到属性中。 如果您不需要多态行为,那么选项3是可以的。但是,您应该记住,当在运行时访问属性MyInt时,派生类对返回的值没有控制权。基类本身能够返回此值 这就是属性的真正多态实现的外观,允许派生类进行控制 你可以这样做
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; } }
}
虚拟让你得到一个属性,一个做某事的实体,并且仍然让子类覆盖它。我这样做了
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
当您想要有一个带有实现的抽象类时,示例实现。子类必须:
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
}
静态是一个糟糕的词汇选择,因为它意味着值在类的所有实例之间共享,当然不是。作为旁白,我真的认为父亲应该应用公式“Y”,母亲在逻辑上应该应用公式“X”。如果我想在父级中提供一个默认实现,而不是抽象的,该怎么办?@AaronFranke制作s
Son son = new Son();
//son.MyInt will equal 2
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
}