Абстрактні властивості в базовому класі змусити програміста визначити його


10

Я кодую схему стану для вбудованого пристрою. У мене є базовий / абстрактний клас під назвою State, і тоді кожен дискретний (конкретний) клас класу реалізує абстрактний клас класу.

У державному класі я маю кілька абстрактних методів. Якщо я не реалізую абстрактні методи в дискретному (конкретному) класі, Visual Studio видасть помилку приблизно так:

... Помилка 1 'myConcreteState' не реалізує успадкований абстрактний член 'myAb абстрактState'

Тепер: я намагаюся створити властивість String для кожного стану під назвою StateName. Щоразу, коли я створюю новий конкретний клас, мені потрібно визначати StateName. Я хочу, щоб VS видав помилку, якщо я не використовую її. Чи є простий спосіб це зробити?

Я спробував це в абстрактному / базовому класі:

public abstract string StateName { get; set; }

Але мені не потрібно застосовувати методи Get and Set у кожній державі.

Переглянуте запитання: В ідеальній ситуації для кожного класу State потрібно було б визначати StateName і бути успадкованим від абстрактного базового класу.

StateName = "MyState1"; //or whatever the state's name is

Якщо цього твердження відсутнє, Visual Studio генерує помилку, як описано вище. Це можливо і якщо так, то як?


2
Я не розумію питання.
Бен Ааронсон

Я думаю, що "правильний" спосіб зробити це - мати захищений конструктор базового класу, який вимагає ім'я стану в якості параметра.
Роман Райнер

@RomanReiner Я думав про те, щоб зробити це також. Але це здається зайвим, тому що щоразу, коли змінювати / називати стан, мені доведеться вводити ім’я.
GisMofx

@BenAaronson Я уточнив своє питання внизу.
GisMofx

Чи ім'я стану постійне на тип чи константа на екземпляр?
Роман Райнер

Відповіді:


16

Я думаю, що "правильний" спосіб зробити це - мати захищений конструктор базового класу, який вимагає ім'я стану в якості параметра.

public abstract class State
{
    private readonly string _name;

    protected State(string name)
    {
        if(String.IsNullOrEmpty(name))
            throw new ArgumentException("Must not be empty", "name");

        _name = name;
    }

    public string Name { get { return _name; } }
}

Потім конкретні стани забезпечують публічний конструктор, який неявно називає конструктор базового класу відповідною назвою.

public abstract class SomeState : State
{
    public SomeState() : base("The name of this state")
    {
        // ...
    }
}

Оскільки базовий клас не виставляє жодних інших конструкторів (ні захищених, ні загальнодоступних), кожен спадковий клас повинен пройти цей єдиний конструктор і, таким чином, потрібно визначити ім'я.

Зауважте, що вам не потрібно вказувати ім'я, коли ви створюєте конкретний стан, оскільки його конструктор дбає про це:

var someState = new SomeState(); // No need to define the name here
var name = someState.Name; // returns "The name of this state"

1
Дякую! Насправді мій конструктор базового класу зараз бере додатковий аргумент; тому у мене є два входи. Як тільки я це налаштував, я отримую помилки, що мені потрібен додатковий аргумент у конструкторах державного класу ...:base(arg1, arg2)! Це рішення, яке я шукав. Це дійсно допомагає підтримувати кодування мого штату більш послідовним.
GisMofx

3

Що стосується C # 6 (я вважаю - C #: Новий та вдосконалений C # 6.0 ), ви можете створити лише властивості getter. Таким чином, ви можете оголосити базовий клас таким чином -

public abstract class State
{
    public abstract string name { get; }

    // Your other functions....
}

І тоді у своєму підкласі ви можете реалізувати Stateтак -

public class SomeState : State
{
    public override string name { get { return "State_1"; } }
}

Або навіть акуратніше за допомогою лямбда-оператора -

public class SomeState : State
{
    public override string name => "State_1";
}

Обидва завжди повертатимуться "State_1" і будуть незмінні.


1
це може бути записано, public override string name { get; } = "State_1";що потім не потрібно переоцінювати значення кожного разу.
Дейв Кузен

2

Ваші вимоги - це суперечність:

Я намагаюся створити властивість String для кожної держави під назвою StateName.

vs.

Але мені не потрібно все це реалізовувати в кожній державі.

Немає мовної функції, яка дозволяє змусити існування члена лише в декількох підкласах. Зрештою, сенс використання суперкласу - покладатися на те, що всі підкласи матимуть усіх членів суперкласу (і, можливо, більше). Якщо ви хочете створити класи, які виступають як, Stateале не мають імені, то, за визначенням, вони повинні (/ можуть) не бути підкласами State.

Вам або потрібно змінити свої вимоги або використовувати щось інше, ніж просте успадкування.

Можливим рішенням з таким кодом, як зробити, можна зробити метод Stateне абстрактним і повернути порожній рядок.


Я думаю, ви вивели це друге твердження поза контекстом, але я уточню. Мені не потрібні методи Get / Set, я просто хочу StateName = "MyState1"в кожному штаті. Якщо у мене немає такого твердження в класі стану, то в ідеалі Visual Studio генеруватиме помилку.
GisMofx

3
@GisMofx Якщо ви хочете змусити ім'я стану у кожному підкласі, зробіть отримання абстрактним, але не вимагайте стану. Тоді кожен підклас повинен буде надати return "MyState1"як їх реалізацію, і може додати змінне сховище для значення, якщо воно потребує. Але вам потрібно уточнити вимоги у питанні - чи вимагає кожен тип штату StateName, який може бути прочитаний, і чи потребує будь-який тип штату його мутації після створення?
Піт Кіркхем

@PeteKirkham кожен стан вимагає імені, воно не повинно бути вимкнено після створення. Назви держав є статичними / незмінними.
GisMofx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.