Почему я не могу иметь защищенных членов интерфейса? - PullRequest
66 голосов
/ 05 февраля 2009

Какой аргумент против объявления членов защищенного доступа на интерфейсах? Это, например, неверно:

public interface IOrange
{
    public OrangePeel Peel { get; }
    protected OrangePips Seeds { get; }
}

В этом примере интерфейс IOrange гарантирует, что разработчики по крайней мере предоставят экземпляр OrangePips своим наследникам. Если разработчик хотел, они могли бы расширить область действия до полного public:

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    public OrangePips Seeds { get { return new OrangePips(6); } }
}

Назначение protected членов на интерфейсах заключается в предоставлении контракта поддержки для наследников (подклассы), например:

public class SpecialNavelOrange : NavelOrange
{
    ...
    // Having a seed value is useful to me.
    OrangePips seeds = this.Seeds; 
    ...
}

(правда, это не сработает в течение struct с)

Я не вижу особого смысла в модификаторах private или internal в интерфейсах, но поддержка модификаторов public и protected кажется вполне разумной. <Ч />

Я попытаюсь объяснить полезность protected членов на interface с, отделив их от interface с целиком:

Давайте представим новое ключевое слово C #, support, для принудительного исполнения контрактов наследников, чтобы мы объявляли вещи следующим образом:

public support IOrangeSupport
{
    OrangePips Seeds { get; }
}

Это позволит нам заключать классы для предоставления защищенных членов их наследникам:

public class NavelOrange : IOrange, IOrangeSupport
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

Это не особенно полезно, потому что классы уже подразумевают этот контракт, предоставляя protected членов в первую очередь.

Но тогда мы могли бы также сделать это:

public interface IOrange : IOrangeSupport
{
   ...
}

Таким образом, применяя IOrangeSupport ко всем классам, которые реализуют IOrange, и требуя от них предоставления определенных protected членов - что мы в настоящее время не можем сделать.

Ответы [ 12 ]

57 голосов
/ 05 февраля 2009

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

public interface IOrange
{
    OrangePeel Peel { get; }
}

public abstract class OrangeBase : IOrange
{
    protected OrangeBase() {}
    protected abstract OrangePips Seeds { get; }
    public abstract OrangePeel Peel { get; }
}

public class NavelOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return new OrangePips(6); } }
}

Edit: Справедливо утверждать, что если у нас есть PlasticOrange, производный от класса Ornament, он может реализовать только IOrange, а не защищенный метод Seeds. Это хорошо. Интерфейс по определению - это контракт между вызывающей стороной и объектом, а не между классом и его подклассами. Абстрактный класс настолько близок, насколько мы подходим к этой концепции. И это нормально. По сути, вы предлагаете еще одну конструкцию в языке, с помощью которой мы можем переключать подклассы из одного базового класса в другой, не нарушая сборки. Для меня это не имеет смысла.

Если вы создаете подкласс класса, этот подкласс является специализацией базового класса. Он должен быть полностью осведомлен о любых защищенных членах базового класса. Но если вам вдруг захочется отключить базовый класс, то не имеет смысла, чтобы подкласс работал с любым другим IOrange.

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

48 голосов
/ 05 февраля 2009

Не могу понять, почему так хочется. Если вы хотите, чтобы производный класс предоставлял реализацию конкретного метода, перейдите к абстрактным базовым классам. Интерфейсы - это просто интерфейсы. Публичный контракт, больше ничего. Думайте об интерфейсе как о спецификации, которая описывает, как реализация должна выглядеть для внешнего мира. В спецификации для двухконтактного разъема не указано (по крайней мере, я так полагаю), какой должна быть его внутренняя структура. Он просто должен быть совместим по интерфейсу с разъемом. Plug
(источник: made-in-china.com )

15 голосов
/ 05 февраля 2009

Потому что это не имеет смысла. Интерфейс является публично доступным контрактом. Я IThing, поэтому я буду выполнять IThing методы, если спросят. Вы не можете попросить IThing подтвердить, что он выполняет методы, о которых он не может вам рассказать.

9 голосов
/ 05 февраля 2009

Существуют интерфейсы, позволяющие людям получать доступ к вашему классу, не зная, какова конкретная реализация. Это полностью отделяет реализацию от контракта передачи данных.

Следовательно, все в интерфейсе должно быть общедоступным. Непубличные члены полезны только в том случае, если у вас есть доступ к реализации, и, следовательно, вы не вносите значительный вклад в определение интерфейса.

7 голосов
/ 05 февраля 2009

Члены интерфейса являются публичным API; такие вещи, как protected и т. д., являются деталями реализации - а интерфейсы не имеют никакой реализации. Я подозреваю, что вы ищете явную реализацию интерфейса:

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    OrangePips IOrange.Seeds { get { return null; } }
}
1 голос
/ 01 ноября 2017

Интерфейс похож на форму ключа.

enter image description here

Это не ключ.

Это не замок.

Это просто тонкий контактный пункт.

По этой причине все члены интерфейса (который определяет форму ключа) должны быть открытыми.

Чтобы ключ открывал замок, важно, чтобы они имели одинаковую форму.

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

В противном случае, делая его (интерфейс) внутренним , вы не позволите другим создавать совместимые замки или совместимые ключи.

1 голос
/ 05 февраля 2009

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

0 голосов
/ 12 июня 2012

Любой класс, который реализует интерфейс .net, должен включать реализации всех членов интерфейса. Кроме того, любой класс может предоставлять производному классу любые члены, которые он пожелает. Требование о том, что реализация интерфейса должна включать член, который будет использоваться только из производных классов, не будет иметь никакой полезной цели, если только (1) такой член не будет виден чему-то вне интерфейса или (2) реализации интерфейса могут использовать члены, которых они сами не определили. Если бы интерфейсам было разрешено включать вложенные классы (которые могли бы получить доступ к protected членам интерфейсов), тогда protected члены интерфейса имели бы смысл. Действительно, они могут быть очень полезны, если класс, вложенный в интерфейс, может определять методы расширения для этого интерфейса. К сожалению, такого объекта не существует.

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

0 голосов
/ 05 февраля 2009

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

0 голосов
/ 05 февраля 2009

В современном дизайне интерфейсов есть здравый смысл: он предлагает разработчикам большую гибкость. Помните, что очень часто интерфейсы пишутся программистами фреймворка, а разработчики - разные люди. Для обеспечения реализации было бы излишне суровым.

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