Переопределение полей или свойств в подклассах

У меня есть абстрактный базовый класс, и я хочу объявить поле или свойство, которое будет иметь другое значение в каждом классе, который наследуется от этого родительского класса.

Я хочу определить его в базовом классе, поэтому я могу ссылаться на него в методе базового класса - например, переопределить ToString, чтобы сказать "Этот объект имеет тип property/field". У меня есть три способа, которые я могу видеть в этом, но мне было интересно - какой лучший или приемлемый способ сделать это? Новый вопрос, извините.

Вариант 1:
Используйте абстрактное свойство и переопределите его в унаследованных классах. Это может быть принудительным (вы должны переопределить его), и это чисто. Но, кажется, немного неправильно возвращать значение жесткого кода, а не инкапсулировать поле, и это несколько строк кода, а не просто. Я также должен объявить тело для "набора", но это менее важно (и, вероятно, есть способ избежать того, чего я не знаю).

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

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

Вариант 2
Я могу объявить публичное поле (или защищенное поле) и явно переопределить его в унаследованном классе. Пример ниже даст мне предупреждение использовать "новое", и я, вероятно, могу это сделать, но он чувствует себя не так, и он ломает полиморфизм, и это в целом. Не кажется хорошей идеей...

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, поскольку он является единственным безопасным вариантом, но я просто разбираюсь с С# и хотел спросить об этом людей с большим опытом.

Ответ 1

Из трех решений только Вариант 1 является полиморфным.

Поля не могут быть переопределены. Именно поэтому Вариант 2 возвращает предупреждение новое.

Решение для предупреждения заключается не в добавлении "нового" ключевого слова, а в реализацию варианта 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 */ }
    }
}

Ответ 2

Вариант 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)
    {
    }
}

Это, вероятно, наиболее подходящий подход, если значение не меняется в течение срока действия экземпляра.

Ответ 3

вариант 2 - плохая идея. Это приведет к чему-то, что называется затенением; В основном у вас есть два разных члена "MyInt" , один в матери, а другой в дочери. Проблема в том, что методы, которые реализуются в матери, будут ссылаться на мать "MyInt" , а методы, реализованные в дочери, будут ссылаться на дочь "MyInt" . это может привести к серьезным проблемам с читабельностью и путанице в дальнейшем.

Лично я считаю, что лучший вариант - 3; потому что он обеспечивает четкую централизованную ценность и может называться внутренне детьми без хлопот по определению их собственных полей, что является проблемой с вариантом 1.

Ответ 4

Вы можете определить что-то вроде этого:

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

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

Установив значение как только для чтения, оно гарантирует, что значение для этого класса не изменится для времени жизни объекта.

Я предполагаю, что следующий вопрос: зачем вам это нужно?

Ответ 5

Вы могли бы сделать это

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 позволяет получить свойство, тело, которое что-то делает, и все же позволяет подклассам переопределять его.

Ответ 6

Если вы создаете класс и хотите, чтобы для свойства существовало базовое значение, используйте ключевое слово 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

Ответ 7

Я бы пошел с вариантом 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; }
    }
}

Ответ 8

Вы можете перейти с опцией 3, если вы измените свой абстрактный базовый класс, чтобы потребовать значение свойства в конструкторе, вы не пропустите ни одного пути. Я бы действительно рассмотрел этот вариант.

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

}

Конечно, у вас все еще есть возможность сделать поле частным, а затем, в зависимости от необходимости, подвергать защищенное или публичное свойство getter.

Ответ 9

Я сделал это...

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

Таким образом, вы все равно можете использовать поля.

Ответ 10

Пример реализации, когда вы хотите иметь абстрактный класс с реализацией. Подклассы должны:

  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
    }