161

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

我想在基类中定义它,这样我就可以在基类方法中引用它——例如覆盖 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#,并想问有更多经验的人。

4

10 回答 10

147

在三个解决方案中,只有选项 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 */ }
    }
}
于 2008-11-29T12:43:30.917 回答
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)
    {
    }
}

如果值在实例的生命周期内不发生变化,这可能是最合适的方法。

于 2008-11-28T17:11:37.477 回答
12

你可以这样做

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 让你得到一个属性,一个做某事的主体,仍然让子类覆盖它。

于 2013-11-13T19:47:38.083 回答
7

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

就个人而言,我认为最好的选择是 3;因为它提供了一个清晰的集中值,并且可以由孩子在内部引用,而无需定义自己的字段——这是选项 1 的问题。

于 2008-11-28T17:33:04.530 回答
4

你可以这样定义:

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

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

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

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

于 2008-11-28T17:08:58.293 回答
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
于 2018-07-26T19:20:29.453 回答
0

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

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

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

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

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

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}
于 2008-11-28T17:05:36.320 回答
0

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

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

}

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

于 2008-11-28T17:06:22.493 回答
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";
        }
    }
}

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

于 2015-11-02T20:30:25.507 回答
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
    }
于 2019-07-23T11:18:21.403 回答