覆盖子类中的字段或属性


145

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

我想在基类中定义它,以便可以在基类方法中引用它-例如,重写ToString表示“此对象的属性/字段类型”。我可以看到三种方式来执行此操作,但是我想知道-最佳或公认的方式是什么?新手问题,对不起。

选项1:
使用抽象属性,并在继承的类上重写它。这得益于强制执行(您必须覆盖它)并且它很干净。但是,返回一个硬代码值而不是封装一个字段感觉有点错误,这是几行代码而不是仅仅几行。我还必须声明一个用于“ set”的主体,但这并不重要(而且可能有一种避免我不知道的主体的方法)。

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

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

选项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#,并想问更多有经验的人。


抽象public int MyInt {get; set;} =>公共抽象字符串IntentName {get; 集;}:D
Navid Golforoushan

Answers:


136

在这三种解决方案中,只有选项1多态的

字段本身不能被覆盖。这正是选项2返回 关键字警告的原因。

解决该警告的方法不是附加“ 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 */ }
    }
}

153
顺便说一句,我真的认为父亲应该使用公式“ Y”,而母亲在逻辑上应该使用“ X”。
彼得-恢复莫妮卡2014年

4
如果我想在Parent中提供默认实现而不是抽象的怎么办?
亚伦·弗兰克

@AaronFranke签名:public virtual int MyInt {get; }
泰德·比格姆

@ Peter-ReinstateMonica那将是“遗传编程”
devinbost

18

选项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)
    {
    }
}

如果值在实例的生存期内没有变化,那可能是最合适的方法。


我们可以这样说,现在不是在此基础上正确msdn.microsoft.com/en-us/library/9fkccyh4.aspx MSDN文章显示,你可以覆盖性能
codingbiz

1
@codingbiz:我的答案在哪里谈论财产?字段和属性不是一回事。
乔恩·斯基特

@codingbiz:(我的回答当然关于属性的-但是从来没有说过您不能覆盖它们。它说-并说了-您不能覆盖字段,这仍然是正确的。)
Jon Skeet

7

选项2是个坏主意。这将导致阴影。基本上,您有两个不同的“ MyInt”成员,一个在母亲中,另一个在女儿中。这样做的问题是,在母亲中实现的方法将引用母亲的“ MyInt”,而在女儿中实现的方法将引用女儿的“ MyInt”。这可能会导致一些严重的可读性问题,并在以后产生混乱。

我个人认为,最好的选择是3。因为它提供了明确的集中式价值,并且可以在内部供儿童参考,而不必麻烦定义自己的字段-这是选项1的问题。


6

你可以这样做

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让您获得一个可以执行某些操作的主体的属性,并且仍然允许子类覆盖它。


4

您可以定义以下内容:

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

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

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

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


静态是单词的不好选择,因为静态意味着该值随后在该类的所有实例之间共享,当然不是。
温斯顿·史密斯

3

如果要构建类,并且希望该属性具有基值,请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

0

我会选择选项3,但是有一个抽象的setMyInt方法,子类被强制实现。这样,您就不会遇到派生类忘记在构造函数中进行设置的问题。

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

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

顺便说一句,如果没有指定set,则使用选项一;在您的抽象基类属性中,派生类将不必实现它。

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

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

0

如果修改抽象基类以在构造函数中要求属性值,则可以使用选项3,而不会丢失任何路径。我真的会考虑这个选项。

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

}

当然,您仍然可以选择将字段设为私有,然后根据需要公开受保护的或公共财产的获取者。


0

我做了这个

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

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


我觉得这很适合作为原型或演示的临时解决方案。
Zimano

0

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

  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
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.