Почему незаконно иметь частный сеттер в явной реализации интерфейса только для гейтеров?
Я склонен поддерживать явные реализации интерфейса над неявными, поскольку я думаю, что программирование против интерфейса в отличие от реализации, как правило, предпочтительнее, а также при работе с веб-службами часто является необходимостью.
Тем не менее, мне было интересно, почему следующее является незаконным с явным объявлением интерфейса и юридическим с неявным:
interface IConnection
{
string ConnectionString { get; }
}
class Connection1 : IConnection
{
// private set is illegal, won't compile
string IConnection.ConnectionString { get; private set; }
}
class Connection2 : IConnection
{
// private set is legal now, it is not part of the interface
string ConnectionString { get; private set; }
}
Я знаю, как это исправить, поскольку законно иметь как явный, так и неявный интерфейс, плюс я могу сделать реализацию неявного интерфейса полностью закрытой.
Тем не менее, мне интересно, почему это объясняется. Поскольку технически, внутренне скомпилированный частный метод set_IConnection_ConnectionString
не обязательно должен быть частью интерфейса, не так ли? Его можно просто рассматривать как вспомогательный сеттер, а не часть интерфейса, как это происходит в неявной ситуации реализации.
Обновление: как бонус, казалось бы, запутанный, и, на мой взгляд, не совсем правильная ошибка компиляции, которую вы получаете, следующая:
Модификатор доступности для доступа должен быть более строгим, чем свойство Connection1.ConnectionString
Извините, более ограничительный, чем private
, как... что?
Ответы
Ответ 1
Единственный способ вызвать явного члена интерфейса - передать объект в правильный интерфейс, а затем вызвать элемент на этом интерфейсе. Но как только вы набрали IConnection
, IConnection.ConnectionString
не имеет сеттера.
Таким образом, нет способа вызвать этот частный метод setter.
Ответ 2
Проблема заключается в том, что когда член интерфейса объявляется явно, компилятор генерирует реализацию private
с "непроизносимым" именем и не предоставляет никаких средств, посредством которых код - даже в пределах класса реализации - ссылается на этот реализация.
В принципе, когда говорят void IFoo.Moo()
, говорят, что не нужно определять имя Moo
в пределах класса; следовательно, компилятор не будет. Для того, чтобы private set
работал, член должен был быть "объявляемым" именем, а тот факт, что элемент был явно реализован, воспринимается как признак того, что имя не должно быть Moo
.
На практике средство здесь, вероятно, такое же, как и во многих других случаях, когда необходимо иметь реализацию интерфейса, чье имя является произносимым, но которое открыто не раскрывается под его именем: объявить реализацию интерфейса, которая ничего не делает, кроме цепочки другим членам, имеющим надлежащую доступность, например если производные классы не должны влиять на значение значение:
private readonly int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}
или, если производные классы должны иметь возможность воздействовать на него, либо
protected int _foo = whatever;
public int IFOO.Foo { get {return _foo;}}
или
private int _foo = whatever;
protected virtual int setFoo(int value) { _foo = value; }
protected virtual int getFoo() { return _foo; }
public int IFOO.Foo { get {return getFoo();}}
В vb.net интерфейсы могут быть реализованы с использованием защищенных членов класса, но С# не предлагает такого средства.
Ответ 3
Я думаю, что суть проблемы в том, что интерфейс имеет только то, что ему нужно. Если он не является общедоступным, он, естественно, не является частью интерфейса. Поэтому, когда вы явно реализуете интерфейс, он не существует.
В неявном случае ваш код подходит для интерфейса, но он не полностью ограничен этим. При необходимости вы можете добавить больше вещей.
Получение информации о том, почему это требуется, потребует от разработчика ответа на этот вопрос. Однако мне кажется логичным: , если он не является частью интерфейса, вы не можете реализовать/получить доступ к нему как часть интерфейса.
Ответ 4
Объявление свойства - это атомная вещь, содержащая геттер и сеттер. Он должен соответствовать интерфейсу.
Если вы позволите это, то, очевидно, геттер и сеттер рассматриваются как разные вещи. В этом случае нет никакой пользы, чтобы ограничить его рядовыми. В этом случае интерфейс просто диктует, что должно быть свойство, которое можно прочитать, и вы также можете сделать его доступным для записи.
В любом случае, по-видимому, это дизайнерское решение сделать его атомарным.
Ответ 5
Возможно, явная реализация интерфейса не должна рассматриваться как часть самого класса, а скорее как своего рода адаптер от вашего интерфейса к классу. Учитывая эту точку зрения; рассмотрим следующую реализацию:
public interface I {
string S { get; set; }
}
class C : I {
public C() {
this.S = "Hello World";
//compile error: explicit implementation not accessible
}
string I.S { get; set; }
}
В классе C свойство S даже не является частным образом доступным, поскольку оно является явной реализацией. Не будет ли плохой дизайн в первую очередь для того, чтобы конкретная реализация поля не была доступна самой реализации?
Кроме того, в вашем примере создание сеттера для явного импликации никогда не будет доступно; поскольку свойство доступно только при передаче в интерфейс IConnection
. Там он имеет только приемник.