В C # при реализации интерфейса все члены неявно открыты. Разве не было бы лучше, если бы мы могли указать модификатор доступности (protected
, internal
, кроме private
, конечно), или вместо этого мы просто использовали бы абстрактный класс?
Непубличные члены для интерфейсов C #
Ответы (9)
Если интерфейс является внутренним, все его члены будут внутренними по отношению к сборке. Если вложенный интерфейс защищен, только подклассы внешнего класса могут получить доступ к этому интерфейсу.
Внутренние члены для интерфейса вне его объявляющей сборки были бы бессмысленными, как и защищенные члены для интерфейса вне его объявляющего внешнего класса.
Задача интерфейса - описать контракт между реализующим типом и пользователями интерфейса. Внешние вызывающие абоненты не будут заботиться и не должны заботиться о реализации, для чего нужны внутренние и защищенные члены.
Для защищенных членов, которые вызываются базовым классом, абстрактные классы - это способ указать контракт между базовыми классами и классами, которые наследуются от них. Но в этом случае детали реализации обычно очень важны, если только это не вырожденный чистый абстрактный класс (где все члены абстрактны), и в этом случае защищенные члены бесполезны. В этом случае используйте интерфейс и сохраните единственный базовый класс для реализации типов на выбор.
internal
членов. Если бы сборка Foo могла содержать интерфейс IWoozle
с internal
членами, и если бы она содержала пять реализаций IWoozle
, код где угодно мог бы передавать экземпляры IWoozle
и использовать его общедоступные члены, но код, получивший IWoozle
, будет знать, что он был реализован в сборке Фу. Рассмотрим, например, гипотетический интерфейс IImmutableWoozle
. Возможно, что не все реализации могут иметь общий базовый тип, но если никакие реализации не могут существовать вне Foo, тогда ...
- person supercat; 13.01.2012
IImmutableWoozle
не будет какой-то дурацкой изменчивой реализацией.
- person supercat; 13.01.2012
IInternalWoozle
, который реализует IWoozle
, и любой код в Foo, использующий любой IWoozle
, может проверить, реализован ли IInternalWoozle
, чтобы получить доступ к внутренним членам. .NET framework делает именно это местами.
- person Mark Cidade; 13.01.2012
internal
членов, когда один из типов сборки обращается к IWoozle.InternalMember
в реализациях извне сборки.
- person Mark Cidade; 13.01.2012
IInternalWoozle
во внешний мир и возвращает его обратно, должна преобразовать его в IInternalWoozle
(и надеяться, что приведение будет успешным). Можно определить общедоступную структуру WrappedWoozle
с членом internal
типа IInternalWoozle
и использовать этот тип структуры при передаче вещей во внешний мир / из внешнего мира, но в некоторых случаях это добавит дополнительный уровень бокса.
- person supercat; 14.09.2012
IInternalWoozle
, то она может использовать оператор as
, чтобы присвоить его переменной IInternalWoozle
и сначала проверить, является ли он нулевым, и в этом случае он может просто проигнорировать его. Если у структуры есть внутренний член, вам все равно придется проверять значение null.
- person Mark Cidade; 15.09.2012
Вы можете скрыть реализацию интерфейса, явно указав имя интерфейса перед именем метода:
public interface IInterface {
public void Method();
}
public class A : IInterface {
public void IInterface.Method() {
// Do something
}
}
public class Program {
public static void Main() {
A o = new A();
o.Method(); // Will not compile
((IInterface)o).Method(); // Will compile
}
}
public
в члене интерфейса или явно реализованном методе интерфейса. Например, чтобы ваш код работал с .NET 4.5, нужно просто удалить оба public
.
- person Andrew; 05.07.2015
o.Method()
не компилировать? Кто-нибудь может это объяснить? Почему указание имени интерфейса скрывает реализацию интерфейса?
- person Kyle Delaney; 26.01.2017
Не имеет смысла. Интерфейс - это договор с общественностью о том, что вы поддерживаете эти методы и свойства. Придерживайтесь абстрактных классов.
Все ответы здесь более или менее говорят о том, что именно такими и должны быть интерфейсы, это универсальные общедоступные спецификации.
Это самая обсуждаемая тема, поэтому позвольте мне опубликовать два отличных ответа, которые я нашел на SO, когда этот вопрос всплыл у меня в голове.
В этом ответе приводится пример того, как может быть бессмысленным наличие неоднородных спецификаторов доступа для членов интерфейса в производных классах. Код всегда лучше технических описаний.
Для меня самое ужасное в принудительных членах открытого интерфейса заключается в том, что сам интерфейс может быть внутренним по отношению к сборке, но члены, которые он предоставляет, должны быть общедоступными. Джон Скит, к сожалению, объясняет, что это сделано намеренно.
Возникает вопрос, почему интерфейсы не были разработаны так, чтобы иметь закрытые определения для членов. Это может сделать контракт гибким. Это очень полезно при написании сборок, где вы не хотите, чтобы определенные члены классов были доступны за пределами сборки. Я не знаю почему.
Интерфейс - это контракт, которого придерживаются все реализующие классы. Это означает, что они должны придерживаться всего этого или ничего.
Если интерфейс является общедоступным, то каждая часть этого контакта должна быть общедоступной, иначе это будет означать один для друзей / внутренних классов и другое для всего остального.
Либо используйте абстрактный базовый класс, либо (если возможно и практично) внутренний метод расширения в интерфейсе.
internal
, тогда внешний код мог бы получать из сборки ссылки на вещи этого типа и передавать такие ссылки обратно в методы сборки, сохраняя при этом безопасность типов во всем, без внешнего кода, который должен что-либо знать о рассматриваемом классе. К сожалению, такой подход будет работать только в том случае, если ни одно из конкретных производных абстрактного класса не должно наследовать от чего-либо, что не является производным от этого абстрактного класса.
- person supercat; 01.06.2013
Вы можете скрыть почти весь код, реализованный интерфейсами к внешним сборкам.
interface IVehicle
{
void Drive();
void Steer();
void UseHook();
}
abstract class Vehicle // :IVehicle // Try it and see!
{
/// <summary>
/// Consuming classes are not required to implement this method.
/// </summary>
protected virtual void Hook()
{
return;
}
}
class Car : Vehicle, IVehicle
{
protected override void Hook() // you must use keyword "override"
{
Console.WriteLine(" Car.Hook(): Uses abstracted method.");
}
#region IVehicle Members
public void Drive()
{
Console.WriteLine(" Car.Drive(): Uses a tires and a motor.");
}
public void Steer()
{
Console.WriteLine(" Car.Steer(): Uses a steering wheel.");
}
/// <summary>
/// This code is duplicated in implementing classes. Hmm.
/// </summary>
void IVehicle.UseHook()
{
this.Hook();
}
#endregion
}
class Airplane : Vehicle, IVehicle
{
protected override void Hook() // you must use keyword "override"
{
Console.WriteLine(" Airplane.Hook(): Uses abstracted method.");
}
#region IVehicle Members
public void Drive()
{
Console.WriteLine(" Airplane.Drive(): Uses wings and a motor.");
}
public void Steer()
{
Console.WriteLine(" Airplane.Steer(): Uses a control stick.");
}
/// <summary>
/// This code is duplicated in implementing classes. Hmm.
/// </summary>
void IVehicle.UseHook()
{
this.Hook();
}
#endregion
}
Это проверит код.
class Program
{
static void Main(string[] args)
{
Car car = new Car();
IVehicle contract = (IVehicle)car;
UseContract(contract); // This line is identical...
Airplane airplane = new Airplane();
contract = (IVehicle)airplane;
UseContract(contract); // ...to the line above!
}
private static void UseContract(IVehicle contract)
{
// Try typing these 3 lines yourself, watch IDE behavior.
contract.Drive();
contract.Steer();
contract.UseHook();
Console.WriteLine("Press any key to continue...");
Console.ReadLine();
}
}
Интерфейсы не имеют модификаторов доступа в своих методах, поэтому они остаются открытыми для любого подходящего модификатора доступа. Это имеет цель: это позволяет другим типам определять, какие методы и свойства доступны для объекта, следующего за интерфейсом. Предоставление им защищенных / внутренних средств доступа сводит на нет цель интерфейса.
Если вы непреклонны в том, что вам нужно предоставить модификатор доступа для метода, либо оставьте его вне интерфейса, либо, как вы сказали, используйте абстрактный класс.
Я знаком с Java больше, чем с C #, но зачем вам частный член внутри интерфейса? У него не могло быть никакой реализации, и он был бы невидим для реализации классов, поэтому был бы бесполезен. Существуют интерфейсы, определяющие поведение. Если вам нужно поведение по умолчанию, используйте абстрактный класс.
На мой взгляд, это нарушает инкапсуляцию. Я должен реализовать методы как общедоступные, затем я реализую интерфейс. Я не вижу причин навязывать публике класс, реализующий интерфейс. (c #)