Почему я не могу иметь защищенных членов интерфейса?
Какой аргумент против объявления членов защищенного доступа на интерфейсах? Это, например, неверно:
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
s)
Я не вижу много причин для private
или же internal
модификаторы в интерфейсах, но поддерживающие оба public
а также protected
Модификаторы кажутся вполне разумными.
Я собираюсь попытаться объяснить полезность protected
участники на interface
s, отделяя их от 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
члены - это не то, что мы можем сделать в настоящее время.
15 ответов
Я думаю, что каждый забил точку интерфейса, имеющего только общедоступных участников, никаких деталей реализации. То, что вы ищете, это абстрактный класс.
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); } }
}
Изменить: Справедливо утверждать, что если у нас есть PlasticOrange, производный от класса Ornament, он может реализовывать только IOrange, а не защищенный метод Seeds. Это хорошо. Интерфейс по определению - это контракт между вызывающей стороной и объектом, а не между классом и его подклассами. Абстрактный класс настолько близок, насколько мы подходим к этой концепции. И это нормально. По сути, вы предлагаете еще одну конструкцию языка, с помощью которой мы можем переключать подклассы из одного базового класса в другой, не нарушая сборки. Для меня это не имеет смысла.
Если вы создаете подкласс класса, этот подкласс является специализацией базового класса. Он должен быть полностью осведомлен о любых защищенных членах базового класса. Но если вы вдруг захотите отключить базовый класс, то нет смысла, чтобы подкласс работал с любым другим IOrange.
Я полагаю, у вас есть справедливый вопрос, но это похоже на угловой случай, и я не вижу никакой пользы от этого, если честно.
Не могу понять, почему так хочется. Если вы хотите, чтобы производный класс предоставлял реализацию конкретного метода, перейдите к абстрактным базовым классам. Интерфейсы - это просто интерфейсы. Публичный контракт, больше ничего. Думайте об интерфейсе как о спецификации, которая описывает, как реализация должна выглядеть для внешнего мира. В спецификации для двухконтактного разъема не указано (по крайней мере, я так полагаю), какой должна быть его внутренняя структура. Он просто должен быть совместим по интерфейсу с разъемом.
(источник: made-in-china.com)
Потому что это не имеет смысла. Интерфейс - это публично открытый контракт. Я IThing, поэтому я буду выполнять IThing методы, если спросят. Вы не можете попросить IThing подтвердить, что он выполняет методы, о которых он не может вам рассказать.
Начиная с C# 8.0 (сентябрь 2019 г.), у вас может быть модификатор доступа внутри интерфейса.
Проверьте эти изменения в интерфейсе c # 8.0
Обновите интерфейсы с помощью методов интерфейса по умолчанию в C# 8.0
См. Эти сообщения
Существуют интерфейсы, позволяющие людям получать доступ к вашему классу, не зная, какова конкретная реализация. Это полностью отделяет реализацию от контракта передачи данных.
Поэтому все в интерфейсе должно быть общедоступным. Непубличные члены полезны только в том случае, если у вас есть доступ к реализации, и, следовательно, они не вносят значительный вклад в определение интерфейса.
Члены интерфейса являются публичным API; вещи как protected
и т.д. - детали реализации - и интерфейсы не имеют никакой реализации. Я подозреваю, что вы ищете явную реализацию интерфейса:
public class NavelOrange : IOrange
{
public OrangePeel Peel { get { return new OrangePeel(); } }
OrangePips IOrange.Seeds { get { return null; } }
}
Интерфейс похож на форму ключа.
Это не ключ.
Это не замок.
Это просто тонкий контактный пункт.
По этой причине все члены интерфейса (который определяет форму ключа) должны быть открытыми.
Чтобы ключ открывал замок, важно, чтобы они имели одинаковую форму.
Делая форму (интерфейс) общедоступной, вы можете позволить другим создавать совместимые блокировки или совместимые ключи.
В противном случае, сделав его (интерфейс) внутренним, вы не позволите другим создавать совместимые блокировки или совместимые ключи.
Для записи: начиная с C # 8.0 интерфейсы теперь могут содержать
- защищенные члены
- частные члены
- реализация
Что в итоге представляет некоторые функции, ранее доступные только на языках, поддерживающих множественное наследование, например совместное использование реализации нескольких базовых членов.
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/default-interface-methods-versionshttps://jeremybytes.blogspot.com/2019/11/c-8-interfaces-public-private-and.html
В отличие от абстрактных базовых классов, защищенные интерфейсы допускали бы "множественное наследование" (абстрактных классов), которое я нашел бы полезным один или два раза...
Интерфейс - это контракт, который обещает определенную функциональность клиентам. Другими словами, цель интерфейса состоит в том, чтобы иметь возможность преобразовывать в него тип и передавать его таким же образом в код, который нуждается в функциях, гарантированных этим интерфейсом. Поскольку клиентский код типа не может получить доступ к защищенным членам этого типа, нет смысла объявлять защищенные элементы в интерфейсе.
В современном дизайне интерфейсов есть здравый смысл: он предлагает разработчикам большую гибкость. Помните, что очень часто интерфейсы пишутся программистами фреймворка, а разработчики - разные люди. Для обеспечения реализации было бы излишне суровым.
Реализуя интерфейс, тип заявляет, что он поддерживает определенный набор методов. Если какой-либо из этих методов не будет общедоступным, он не будет доступен для вызывающих, и, следовательно, тип не будет поддерживать интерфейс, как указано.
Любой класс, который реализует интерфейс.net, должен включать реализации всех членов интерфейса. Кроме того, любой класс может предоставлять производному классу любые члены, которые он пожелает. Требование о том, что реализация интерфейса должна включать в себя член, который будет использоваться только из производных классов, не будет иметь никакой полезной цели, если (1) такой член не будет виден чему-то вне интерфейса или (2) реализации интерфейса могут использовать члены, которых они сами не определили. Если бы интерфейсам было разрешено включать вложенные классы (которые могли бы получить доступ к интерфейсам protected
члены), затем protected
члены интерфейса будут иметь смысл. Действительно, они могут быть очень полезны, если класс, вложенный в интерфейс, может определять методы расширения для этого интерфейса. К сожалению, такой возможности не существует.
Кстати, даже не имея возможности вкладывать классы в интерфейсы, все равно было бы полезно применить internal
модификатор доступа к элементам интерфейса с тем эффектом, что только сборка, в которой определен интерфейс, сможет определить любые реализации для него.
Интерфейс - это все, что может делать определенный объект, поэтому при использовании класса, реализующего этот интерфейс, разработчик будет ожидать, что все члены будут реализованы, поэтому модификатор защищенного доступа ничего не будет значить для интерфейсов.
Интерфейс содержит только открытые члены. Защищенный означает, что все, что вы объявляете, доступно только классу и экземплярам производного класса.