Добавление сеттеров к свойствам в переопределениях

Почему разрешено изменять видимость и наличие геттеров или сеттеров в свойстве при реализации интерфейса?

interface IFoo
{
    string Bar { get; }
}

class RealFoo : IFoo
{
    public RealFoo(string bar)
    {
        this.Bar = bar;
    }

    public string Bar { get; private set; }
}

class StubFoo : IFoo
{
    public string Bar { get; set; }
}

... и не законно делать то же самое при реализации абстрактного класса?

abstract class AbstractFoo : IFoo
{
    public abstract string Bar { get; }
}

class RealFoo : AbstractFoo
{
    public RealFoo(string bar)
    {
        this.Bar = bar;
    }

    // Cannot override because 'Bar' does not have an overridable set accessor
    public override string Bar { get; private set; }
}

person ndeuma    schedule 19.05.2011    source источник
comment
В вашем примере вы добавляете код к реализации интерфейса, так как не было установщика, но вы меняете видимость в реализации абстрактного класса. Так что это не то же самое.   -  person jv42    schedule 19.05.2011
comment
@jv42 jv42 В реализации не изменяется видимость, поскольку установщик является частным. Ограничение является произвольным и глупым... С тем же успехом можно было бы указать, чтобы переопределение применялось только к получению или установке, когда они не являются частными. Или это может позволить public string Bar { override get; private set; }   -  person Jim Balter    schedule 25.10.2014
comment
@JimBalter Я согласен с тем, что могут быть механизмы для изменения видимости средств доступа. Возможно, вы захотите проверить, что будет в C# 6, я видел кое-что интересное для свойств.   -  person jv42    schedule 26.10.2014
comment
@ jv42 Я уже знаю, что будет в C# 6, но этого там нет. И дело было в том, что ваш комментарий был неправильным.   -  person Jim Balter    schedule 26.10.2014
comment
@JimBalter Ну, тогда у нас разногласия.   -  person jv42    schedule 27.10.2014
comment
@ jv42 Если вы не согласны с тем, что приватность не меняет видимость, то у вас очень серьезная проблема.   -  person Jim Balter    schedule 27.10.2014
comment
@JimBalter Ну, вот мой пользовательский голос спрашивает об этом.   -  person binki    schedule 29.01.2015
comment
@binki Я думаю, что ваш пользовательский пост не соответствует сути. Изменение в C#, позволяющее добавлять [gs]etter, может разрешить только добавление private — и это все, что требуется здесь. Защищенный или общедоступный подразумевает базовый метод, которого не существует.   -  person Jim Balter    schedule 30.01.2015
comment
@JimBalter Я не понимаю разницы между общедоступным сеттером в подклассе и добавлением совершенно нового свойства в подкласс. Интерфейсы и базовые классы предназначены для определения минимума того, что предоставляет реализация/подкласс, а не для ограничения реализации/подкласса (кроме случаев, когда в игру вступает sealed, у которого есть использование). Если мой код использует AbstractFoo, какая разница, если RealFoo.Bar имеет публичный сеттер?   -  person binki    schedule 30.01.2015


Ответы (4)


Интерфейс объявляет, какие общедоступные свойства должен иметь класс (это просто контракт). Это означает, что вам нужно иметь эти свойства, но вы можете добавлять к ним.

Абстрактный класс объявляет фактическую структуру этих свойств. Поэтому, если у вас нет сеттера в абстрактной базе, вы не можете добавить к нему в реализации.
Когда вы пишете модификатор переопределения, он ищет в базовом классе что-то для переопределения.

person Yochai Timmer    schedule 19.05.2011

Возможно, станет яснее, если вы подумаете о геттерах и сеттерах как о методах, которыми они со временем становятся.

В случае интерфейса вы определяете это:

interface IFoo
{
    string GetBar();
}

Что можно прочитать как «все классы, реализующие этот интерфейс, должны включать этот метод». Оба ваших класса делают:

class RealFoo : IFoo
{
    public string GetBar();
    private void SetBar(string value);
}

они также реализуют SetBar(), но это несущественно; они выполнили контракт, определенный интерфейсом, и являются действительными.

С другой стороны, абстрактный класс таков:

abstract class AbstractFoo : IFoo
{
    public abstract string GetBar();
}

Это означает, что все дочерние классы должны предоставлять тело метода для GetBar().

Класс, который вы сделали, таков:

class RealFoo : AbstractFoo
{
    public override string GetBar();
    public override void SetBar(string value);
}

Помещая модификатор override перед методом SetBar, компилятор ожидает найти абстрактную или виртуальную версию в базовом классе. У вас этого нет, поэтому компиляция не работает.

person Martin Harris    schedule 19.05.2011
comment
Это означает, что свойства на самом деле являются просто ярлыками для сеттеров и геттеров и ничего не предполагают о хороших принципах объектно-ориентированного проектирования. - person Marcel Valdez Orozco; 17.03.2013
comment
Таким образом, вы косвенно говорите, что C# должен был бы добавить синтаксис, подобный public string Bar { override get; set; }, и тогда мы были бы менее произвольно ограничены в том, как мы реализуем абстрактные свойства. Это действительно имеет смысл. - person binki; 29.01.2014
comment
@binki Это имеет смысл, поэтому в этих ответах нет, а ограничение является произвольным и имеет плохой дизайн. С# не нужно добавлять синтаксис, он может просто взять синтаксис, предоставленный OP, и обработать его разумно, вместо того, чтобы требовать от нас явного предоставления резервной переменной. - person Jim Balter; 25.10.2014

Абстрактный класс — это класс, который не может быть создан, но должен быть унаследован. Абстрактный класс может быть полностью реализован, но чаще реализован частично или вообще не реализован, тем самым инкапсулируя общую функциональность для унаследованных классов.

Интерфейс, напротив, представляет собой совершенно абстрактный набор членов, который можно рассматривать как определение контракта поведения. Реализация интерфейса полностью оставлена ​​на усмотрение разработчика.

Взято из MSDN http://msdn.microsoft.com/en-us/library/scsyfw1d(v=VS.71).aspx

person Brian Dishaw    schedule 19.05.2011
comment
Хорошо, это означает, что добавление абстрактного базового класса в иерархию классов заставит вас добавить пустую/выбрасывающую исключение реализацию сеттера в некоторые производные классы. Не самое элегантное поведение. - person ndeuma; 19.05.2011
comment
Этот ответ не затрагивает фактически заданный вопрос. - person Jim Balter; 25.10.2014

Согласно спецификации С#

Аксессор, который используется для реализации интерфейса, может не иметь модификатора доступа. Если для реализации интерфейса используется только один метод доступа, другой метод доступа может быть объявлен с модификатором доступа:

public interface I
{
  string Prop { get; }
}
public class C: I
{
  public Prop {
  get { return "April"; }     // Must not have a modifier here
        internal set {...}    // Ok, because I.Prop has no set accessor
  }
}

Это означает, что можно изменить доступ к классу, реализующему интерфейс. Однако абстрактный класс объявляет реализацию, и вы не можете изменить ее с помощью производного класса.

person Marius Bancila    schedule 19.05.2011
comment
abstract свойства не имеют реализации. Что вы подразумеваете под «абстрактный класс объявляет реализацию»? Что абстрактный класс произвольно ограничивает реализацию, а интерфейс не ограничивает произвольно реализацию? - person binki; 29.01.2014
comment
Да, абстрактные классы могут иметь реализацию. Абстрактный класс может не иметь реализации или может быть реализован не полностью. Предполагается, что абстрактный класс используется только для получения других классов. - person Marius Bancila; 30.01.2014
comment
абстрактные классы могут иметь реализацию - @binki сказал, что абстрактные свойства не имеют реализации. Ваш ответ не относится к его комментарию. У С# нет веской причины не разрешать что-то вроде public string Bar { override get; private set; } - person Jim Balter; 25.10.2014