Перевизначити get, але не встановити


78

У мене є абстрактний клас, який визначає a get, але ні set, тому що, що стосується цього абстрактного класу, йому потрібен лише a get.

public abstract BaseClass
{
  public abstract double MyPop
  {get;}
}

Однак у якомусь класі виведення мені потрібна setвластивість, тому я розглядаю цю реалізацію

public class DClass: BaseClass
{
  public override double MyPop
  {get;set;}
}

Проблема в тому, що я отримав повідомлення про помилку компіляції

* .set: неможливо замінити, оскільки *. не має переадресованого набору аксесуарів.

Хоча я думаю, що вищезазначений синтаксис цілком законний.

Будь-яка ідея з цього приводу? Обхідний шлях, або чому це так?

Редагувати: Єдиний підхід, який я можу придумати, - поставити і те, getі інше, setяк в абстрактному класі, і нехай підклас кидає а NotImplementedExceptionякщо setвикликається, і це не потрібно. Це те, що мені не подобається, поряд із спеціальним методом сеттера .


Давайте трохи розберемо це. Вам потрібно побудувати набір класів, які виставляють метод для читання doubleзначення, яке буде реалізоване конкретно в кожному класі. Іноді це значення потрібно буде встановити, і тому деякі з цих класів повинні надавати спосіб встановити це. Чи це правильно? Скільки рівнів успадкування потрібно використовувати?
Кодслеут

1
@Codesleuth: так. Щодо того, скільки рівнів успадкування, я не впевнений, наскільки це стосується питання?
Гравітон

@ Девід, за іронією долі це здається питанням, на яке я не хочу прийняти відповідь.
Graviton

3
Я не розумію, чому це можливо для інтерфейсів, але не для абстрактних класів. Що дає?
BlueRaja - Danny Pflughoeft

Це так гарно. Хтось розуміє, чому це неможливо? властивості get і set дійсно перекладаються на еквіваленти методу десь уздовж шляху компіляції, чи не так? Тож віртуальність цих методів може бути окремими питаннями? редагувати: Думаю, можливо, я знайшов тут свою відповідь: stackoverflow.com/questions/82437/…
Маттіас Нордквіст

Відповіді:


15

Нове у C # 6.0:

Якщо ви викликаєте сеттер лише у своєму конструкторі, ви можете вирішити цю проблему за допомогою властивостей лише для читання.

void Main()
{
    BaseClass demo = new DClass(3.6);
}

public abstract class BaseClass
{
    public abstract double MyPop{ get; }
}

public class DClass : BaseClass
{
    public override double MyPop { get; }
    public DClass(double myPop) { MyPop = myPop;}
}

4
Хоча це правда, це не є гарним загальним рішенням проблеми. Звичайно, буде багато випадків, коли бажаний громадський установник.
Tom Bogle

26

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

public override double MyPop
{
    get { return _myPop; }
}

public void SetMyPop(double value)
{
    _myPop = value;
}

17

Неможливо робити те, що ти хочеш. Ви повинні визначити сеттер у властивості abstract, інакше ви не зможете його перевизначити належним чином.

Єдиний випадок, коли я знаю, де визначається геттер і реалізується ґеттер / сеттер, це використання інтерфейсу:

public interface IBaseInterface
{
    double MyPop { get; }
}

public class DClass : IBaseInterface
{
    public double MyPop { get; set; }
}

12
Непоганий підхід; але іноді вам просто потрібно використовувати абстрактний клас, а не інтерфейс.
Гравітон

@Graviton: Я зазначу, що це рішення стане більш життєздатним, якщо / коли C # запровадить методи інтерфейсу за замовчуванням .
Брайан

8

Якщо BaseClassу вашій власній кодовій базі, ви можете зробити:

abstract public class BaseClass
{
    abstract public double MyPop { get; protected set; }
}

public class DClass : BaseClass
{
    private double _myProp;
    public override double MyProp
    {
        get { return _myProp; }
        protected set { _myProp = value; }
    }
}

РЕДАГУВАТИ: Ви можете піти зробити загальнодоступний метод у DClass SetMyProp(double myProp)тощо. Дизайн класу для вашої моделі домену повинен чітко усвідомлювати або говорити сам за себе, чому ви не можете встановити властивість безпосередньо в базовому класі і чому ви можете це зробити в похідному.


Добре, але як щодо тих підкласів, для яких не потрібно встановлювати MyPop?
Гравітон

Можливо, вони можуть отримувати лише з BaseClass тоді, а не з DClass?
herzmeister

Я не думаю, що це хороша ідея для моїх програм; спадщина повинна бути зарезервована для is aстосунків, а не для обходу мовних обмежень; крім того, я використовую спадщину для інших більш реалістичних цілей у цій заявці.
Гравітон

1
Я насправді ніколи не мав проблем з цією проблемою, і я фанатикую щодо правильності ОО, повірте мені ;-) ... (хоча ОО має обмеження, зауважте) ... Можливо, коли ви розповісте мені про свій точний сценарій, я міг би зробити пропозиція щодо вашої моделі.
herzmeister

1
Я більше думав про реальний приклад, який я хотів би бачити, що б ви хотіли змоделювати. Наприклад, ваші заняття з тваринами, жирафами та левами або ваші заняття з небесним тілом, метеоритами та планетами.
herzmeister

5

Ви впевнені, що робити те, що ви намагаєтесь, було б гарним дизайном, якби ви знайшли спосіб це зробити?

Це дозволило б об’єктам підкласу вносити зміни стану, які об’єкти батьківського класу зробити не можуть. Чи не порушить це Принцип заміщення Ліскова?


2

Ви можете зробити щось подібне:

abstract class TestBase { public abstract int Int { get; } }

class TestDerivedHelper : TestBase
{
    private int _Int;
    public override int Int
    {
        get
        {
            return _Int;
        }
    }

    protected void SetInt(int value)
    {
        this._Int = value;
    }
}

class TestDerived : TestDerivedHelper
{
    public new int Int
    {
        get { return base.Int; }
        set { base.SetInt(value); }
    }
}

Використання TestDerived матиме функціональність, яку ви шукаєте. Єдиним недоліком, який я бачу з цього методу, є те, що вам доводиться реалізовувати кожен абстрактний метод у TestDerivedHelper, але це надає вам більше контролю пізніше.

Сподіваюся, це допомагає. ;)


2

Причина того, що це неможливо, пов’язана з тим, як параметри “Влаштовуються” за допомогою C #. Коли ви визначаєте параметр, C # створює приватний поле, яким неявний геттер та сеттер маніпулюють. Якщо в базовому класі немає установника, неможливо змінити цю змінну з методу, записаного в підкласі (оскільки приватний прапор забороняє доступ навіть підкласам до нього). Зазвичай це те, що замість цього використовується неявний сеттер базового класу.

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


1

Облога

abstract class TestBase
{
    public abstract int Int { get; }
}

class TestDerivedHelper : TestBase
{
    private int _Int;
    public override int Int
    {
        get
        {
            return _Int;
        }
    }

    protected void SetInt(int value)
    {
        this._Int = value;
    }
}

class TestDerived : TestDerivedHelper
{
    public new int Int
    {
        get { return base.Int; }
        set { base.SetInt(value); }
    }
}

Використання TestDerived матиме функціональність, яку ви шукаєте. Єдиним недоліком, який я бачу з цього методу, є те, що вам доводиться реалізовувати кожен абстрактний метод у TestDerivedHelper, але це надає вам більше контролю пізніше.

Я використовую такий підхід і дуже добре для мене працює. Крім того, я також зробив абстрактним свій клас "TestDerivedHelper", тоді всі методи повинні бути реалізовані в класі "TestDerived".


1

Незважаючи на те, що ця тема стара, я позикую своє рішення, на випадок, якщо воно комусь допоможе. Це не моє власне, а базується на відповідях в інших темах SO.

public abstract BaseClass
{
  public double MyPoP { get { return GetMyPoP; } }

  protected abstract double GetMyPoP { get; }
}

public class DClass: BaseClass
{
  public new double MyPoP { get; set; }

  protected override double GetMyPop { get { return MyPoP; } }
}

Це рішення додає додатковий рядок коду для кожного такого властивості, яке потребує модифікації доступу. Однак зовнішній огляд не змінюється і забезпечує необхідну функціональність.


0
public abstract class BaseClass
{
    public abstract double MyPop { get; }
}

public class DClass: BaseClass
{
    private double _myPop = 0;
    public override double MyPop 
    {
        get { return _myPop; }
    }

    // some other methods here that use the _myPop field
}

Якщо вам потрібно встановити властивість ззовні, DClassможливо, було б краще додати сеттер до базового класу.


-2

РЕДАГУВАТИ:

Добре, можливо, я поспішив із цією відповіддю, але зараз я ще трохи подумав.

Чи потрібно використовувати абстрактний базовий клас? Якщо це не потрібно, спробуйте наступне:

public interface ISomeRelevantName
{
    double MyPop { get; }
}

public class DClass : ISomeRelevantName
{
    public double MyPop { get; set; }
}

2
Поскаржиться, що абстрактне властивість MyPopз BaseClassне реалізовано у похідному класі.
Джої

Хм, моя відповідь мене ганьбить: (Я намагаюсь виправити це за допомогою редагування.
Codesleuth

-3

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


6
абстрактний аксесуар не може бути приватним
Graviton

abstractвластивість це getабо setаксесс в privateаналогу protected. Тож у базовому класі ви хочете:public abstract MyProp { get; protected set; }
JoeBrockhaus

-3

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

Що ви можете зробити, це використовувати newключове слово, щоб приховати реалізацію базових класів, але це може бути не те, що ви хочете.


1
Базовий клас є абстрактним і не має реалізації , тому newне може працювати.
Джої

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

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