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

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

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

Вариант 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 # и хотел спросить об этом людей с большим опытом .


person Frans    schedule 28.11.2008    source источник
comment
абстрактный общественный int MyInt {получить; set;} = ›общедоступная абстрактная строка IntentName {get; set;}: D   -  person Navid Golforoushan    schedule 26.06.2019
comment
@ Čamo Редактировать заголовки так, как вы делаете, неуместно. Для этого нужны теги.   -  person Servy    schedule 04.03.2021
comment
Язык должен быть в заголовке. Ни у кого нет времени искать и читать маленькие теги где-то в конце вопроса, а нужно искать множество результатов. Спасибо за понимание   -  person Čamo    schedule 04.03.2021
comment
Если бы язык был в названии, его бы не было в моих результатах поиска по Java. Надеюсь, что в этом есть смысл.   -  person Čamo    schedule 04.03.2021
comment
@ Čamo И все же ваше личное мнение по этому поводу полностью противоречит политике сайта по этому поводу. Просто потому, что вы хотите, чтобы все помещали свои теги в заголовок, не делает его уместным. При поиске учитываются теги, а не только заголовки. Надеюсь, что эти причины имеют смысл, и спасибо за ваше понимание и понимание того, что правила сайта не нарушаются.   -  person Servy    schedule 04.03.2021
comment
Может быть политика неправильная. Что не имеет смысла в моих комментариях? Это не о мнениях, а об истине.   -  person Čamo    schedule 04.03.2021
comment
@ Čamo Если вы хотите, чтобы политика изменилась, предложите изменить политику, не обходите стороной вопросы, связанные с неправильным редактированием. Существует множество продолжительных дискуссий по этому вопросу, которые вы можете прочитать, если вас интересуют объяснения текущей политики, и с которыми вы должны полностью ознакомиться, прежде чем предлагать ее изменить.   -  person Servy    schedule 04.03.2021
comment
Есть только одна правда. По правде говоря, этот вопрос C # находится в моем результате Java. Никогда не читайте теги. Это может быть полезно для поисковой системы, но не для людей.   -  person Čamo    schedule 04.03.2021


Ответы (10)


Из трех решений только вариант 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 */ }
    }
}
person Preets    schedule 29.11.2008
comment
Кстати, я действительно думаю, что Отец должен применять формулу Y, а Мать, логически, X. - person Peter - Reinstate Monica; 17.04.2014
comment
Что, если бы я хотел предоставить реализацию по умолчанию в Parent и не делать ее абстрактной? - person Aaron Franke; 06.12.2018
comment
@AaronFranke Сделайте подпись: public virtual int MyInt {get; } - person Ted Bigham; 10.04.2020
comment
@ Peter-ReinstateMonica Это было бы генетическое программирование - person devinbost; 26.06.2020

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

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

person Jon Skeet    schedule 28.11.2008
comment
Можем ли мы сказать, что это неверно, на основании этого msdn.microsoft.com/en- us / library / 9fkccyh4.aspx В статье msdn показано, что вы можете переопределить свойства - person codingbiz; 03.09.2014
comment
@codingbiz: где в моем ответе говорится о свойствах? Поля и свойства - это не одно и то же. - person Jon Skeet; 03.09.2014
comment
@codingbiz: (Мой ответ сейчас говорит о свойствах, по общему признанию, но он никогда не говорил, что вы не можете их переопределить. Он сказал - и говорит - что вы не можете переопределить поля , что по-прежнему правильно.) - person Jon Skeet; 03.09.2014

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

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

person Joshua G    schedule 13.11.2013

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

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

person Community    schedule 28.11.2008

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

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

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

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

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

person Ant    schedule 28.11.2008
comment
Статический - плохой выбор слов, поскольку он подразумевает, что значение затем распределяется между всеми экземплярами класса, что, конечно, не так. - person Winston Smith; 28.11.2008

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

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

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

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

}

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

person Blair Conrad    schedule 28.11.2008

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

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

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

person 8r13n    schedule 02.11.2015
comment
Мне кажется, что это хорошее решение для создания прототипов или демонстрации. - person Zimano; 11.08.2017

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

  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
    }
person Belomestnykh Sergey    schedule 23.07.2019