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

StackOverflow https://stackoverflow.com/questions/326223

  •  11-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Решение для предупреждения не состоит в том, чтобы добавить & # 8220; new & # 8221; ключевое слово, но для реализации варианта 1.

Если вам нужно, чтобы ваше поле было полиморфным, вам нужно поместить его в свойство.

Вариант 3 можно использовать, если вам не нужно полиморфное поведение. Однако следует помнить, что при доступе к свойству MyInt производный класс не имеет никакого контроля над возвращаемым значением. Базовый класс сам по себе способен возвращать это значение.

Вот как может выглядеть действительно полиморфная реализация вашего свойства, позволяющая производным классам находиться в control .

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 не является начальным - вы не можете переопределить поля, вы можете только скрыть их.

Лично я бы выбрал вариант 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.

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

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

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

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

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

Вы можете сделать это

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

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

Я бы выбрал вариант 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; }
    }
}

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

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

}

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top