Непубличные члены для интерфейсов C #

В C # при реализации интерфейса все члены неявно открыты. Разве не было бы лучше, если бы мы могли указать модификатор доступности (protected, internal, кроме private, конечно), или вместо этого мы просто использовали бы абстрактный класс?


person jfs    schedule 20.08.2008    source источник
comment
Вы можете скрыть интерфейс от API, если реализуете его явно.   -  person epitka    schedule 26.08.2010
comment
Возможно, появится в C # 8.0 jeremybytes.blogspot. ru / 2019/11 /   -  person Doug Domeny    schedule 22.09.2020


Ответы (9)


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

Внутренние члены для интерфейса вне его объявляющей сборки были бы бессмысленными, как и защищенные члены для интерфейса вне его объявляющего внешнего класса.

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

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

person Mark Cidade    schedule 20.08.2008
comment
что вы имеете в виду под пользователями этого интерфейса? Итак, когда вы создаете переменную, скажем, IMyInterface someVar; А потом как-нибудь использовать someVar? - person PositiveGuy; 08.07.2010
comment
Также я могу добавить, что интерфейсы создают шаблон / общность для структуры вашего приложения (классов внутри), что является точкой объятия, даже если вы не открываете интерфейсы внешним клиентам ... верно? - person PositiveGuy; 09.07.2010
comment
Я бы подумал, что для интерфейсов полезно иметь internal членов. Если бы сборка Foo могла содержать интерфейс IWoozle с internal членами, и если бы она содержала пять реализаций IWoozle, код где угодно мог бы передавать экземпляры IWoozle и использовать его общедоступные члены, но код, получивший IWoozle, будет знать, что он был реализован в сборке Фу. Рассмотрим, например, гипотетический интерфейс IImmutableWoozle. Возможно, что не все реализации могут иметь общий базовый тип, но если никакие реализации не могут существовать вне Foo, тогда ... - person supercat; 13.01.2012
comment
... код в Foo, или который верил, что Foo не сделает ничего плохого, мог знать, что IImmutableWoozle не будет какой-то дурацкой изменчивой реализацией. - person supercat; 13.01.2012
comment
В этом случае вы можете создать внутренний интерфейс IInternalWoozle, который реализует IWoozle, и любой код в Foo, использующий любой IWoozle, может проверить, реализован ли IInternalWoozle, чтобы получить доступ к внутренним членам. .NET framework делает именно это местами. - person Mark Cidade; 13.01.2012
comment
Члены интерфейса, доступные только объявляющей сборке, потребуют некоторой ненужной схемы для сопоставления нереализованных internal членов, когда один из типов сборки обращается к IWoozle.InternalMember в реализациях извне сборки. - person Mark Cidade; 13.01.2012
comment
@MarkCidade: проблема с использованием отдельного внутреннего интерфейса заключается в том, что любая процедура, которая передает IInternalWoozle во внешний мир и возвращает его обратно, должна преобразовать его в IInternalWoozle (и надеяться, что приведение будет успешным). Можно определить общедоступную структуру WrappedWoozle с членом internal типа IInternalWoozle и использовать этот тип структуры при передаче вещей во внешний мир / из внешнего мира, но в некоторых случаях это добавит дополнительный уровень бокса. - person supercat; 14.09.2012
comment
Если для подпрограммы важно определить, является ли объект IInternalWoozle, то она может использовать оператор as, чтобы присвоить его переменной IInternalWoozle и сначала проверить, является ли он нулевым, и в этом случае он может просто проигнорировать его. Если у структуры есть внутренний член, вам все равно придется проверять значение null. - person Mark Cidade; 15.09.2012
comment
Если интерфейс является внутренним, все его (общедоступные) члены НЕ будут внутренними по отношению к сборке. Даже если это так, это разрушает саму идею публики! - person nawfal; 24.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
    }
}
person samjudson    schedule 20.08.2008
comment
Просто комментарий: в настоящее время вы не можете использовать модификатор public в члене интерфейса или явно реализованном методе интерфейса. Например, чтобы ваш код работал с .NET 4.5, нужно просто удалить оба public. - person Andrew; 05.07.2015
comment
Эндрю, какие два паблика вы имеете в виду? - person Kyle Delaney; 26.01.2017
comment
Почему бы o.Method() не компилировать? Кто-нибудь может это объяснить? Почему указание имени интерфейса скрывает реализацию интерфейса? - person Kyle Delaney; 26.01.2017
comment
Кайл. Эта нотация (реализация метода с именем интерфейса) называется явной реализацией интерфейса, и я думаю, ее цель - скрыть вызываемый метод извне (кроме явного приведения и последующего вызова). Это очень полезно в некоторых случаях. Например, базовый класс может выполнять единовременную операцию в реализации метода интерфейса. Если метод виден для внешних вызывающих абонентов, они могут вызвать метод более одного раза и нарушить рабочее состояние базового класса. - person Xtro; 27.10.2017

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

person Ishmaeel    schedule 20.08.2008
comment
«Внутренний» модификатор доступа может показаться очень полезным для интерфейсов; интерфейс, который имеет такой модификатор на любом из его членов, может быть реализован только кодом внутри сборки, в которой он объявлен, но может использоваться кодом где угодно. Я вижу множество применений для этого. - person supercat; 13.01.2012
comment
Я не думаю, что интерфейсы предназначены для использования в качестве строго типизированных руководств или каркасов для кода внутри одних и тех же сборок. Я считаю, что их главная цель - взаимодействие с внешним миром. - person Ishmaeel; 17.01.2012
comment
Предположим, сборка определяет BankCheckingAccount, CreditUnionCheckingAccount, BankSavingsAccount и CreditUnionSavingsAccount. Банковские счета имеют функции, отсутствующие в счетах кредитных союзов, и наоборот. У чековых счетов есть функции, которых нет у сберегательных счетов, и наоборот. Было бы полезно иметь типы для текущих счетов, сберегательных счетов, банковских счетов и счетов кредитных союзов, но можно было бы использовать только абстрактные классы для двух из этих четырех. Если бы наследование абстрактных классов было ограничено одной и той же сборкой ... - person supercat; 17.01.2012
comment
... можно было быть уверенным, что CheckingAccount был либо BankCheckingAccount, либо CreditUnionCheckingAccount, а не JoeHackerPhonyCheckingAccount. Было бы полезно иметь возможность предоставить аналогичные гарантии для интерфейса IBankAccount (реализованного BankCheckingAccount и BankSavingsAccount) или интерфейса ICreditUnionAccount (реализованного CreditUnionCheckingAccount и CreditUnionSavingsAccount). [Конечно, нельзя доверять реальную финансовую информацию классовой безопасности - это просто аналогия]. - person supercat; 17.01.2012
comment
Интерфейс - это договор с общественностью о том, что вы поддерживаете эти методы и свойства - я не согласен с этим, я бы сказал, что интерфейс является (или должен быть) контрактом с разработчиком. И я должен иметь возможность принудительно реализовать определенные частные методы и свойства. - person Gavin Williams; 08.02.2019
comment
Вам также понадобится здоровяк с бейсбольной битой, чтобы заставить меня реализовать этот интерфейс. Я бы просто поставил заглушку метода, чтобы выключить компилятор, а затем поместил бы свою реальную реализацию в другой частный метод - знаете ли, просто назло. - person Ishmaeel; 08.02.2019

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

Это самая обсуждаемая тема, поэтому позвольте мне опубликовать два отличных ответа, которые я нашел на SO, когда этот вопрос всплыл у меня в голове.

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

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

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

person nawfal    schedule 24.09.2012

Интерфейс - это контракт, которого придерживаются все реализующие классы. Это означает, что они должны придерживаться всего этого или ничего.

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

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

person Keith    schedule 20.08.2008
comment
Если бы нужно было объявить открытый абстрактный класс, все члены и конструкторы которого были internal, тогда внешний код мог бы получать из сборки ссылки на вещи этого типа и передавать такие ссылки обратно в методы сборки, сохраняя при этом безопасность типов во всем, без внешнего кода, который должен что-либо знать о рассматриваемом классе. К сожалению, такой подход будет работать только в том случае, если ни одно из конкретных производных абстрактного класса не должно наследовать от чего-либо, что не является производным от этого абстрактного класса. - person supercat; 01.06.2013
comment
Интерфейс, содержащий внутренние члены, если бы такое было разрешено, был бы очень похож на вышеупомянутый абстрактный класс, но с тем преимуществом, что он мог быть реализован классами, которые были производными от классов, которые этого не сделали. - 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();
    }
}
person Community    schedule 30.10.2008

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

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

person Jon Limjap    schedule 20.08.2008
comment
разрешение другим (читаемым внешним) типам делать выводы о том, какие методы и свойства доступны для объекта, является желанным выбором? Я бы сказал, что объект должен иметь возможность решать, что должно быть доступно внешнему миру для вывода. - person nawfal; 24.09.2012
comment
Точнее, nawfal - а интерфейс - это контракт, а не объект. Интерфейсы должны использоваться для описания доступного поведения в отличие от определения деталей реализации. - person Jon Limjap; 24.09.2012
comment
Джон, сегодня я понял, что это контракт. Не просто контракт, а скорее публичная спецификация для всего мира. Я подвергал сомнению этот принцип, но когда люди выражаются словами, они звучат так, как будто члены интерфейса являются общедоступными? на которые часто можно найти ответы, являются разновидностями контракта такого рода. @Jon Skeet говорит здесь, что это так, потому что это так. Вот еще одно следствие наличия непубличных членов интерфейса. - person nawfal; 24.09.2012
comment
Но теперь мне интересно, почему определение члена в самом интерфейсе не может быть частным, общедоступным и т. Д. Я до сих пор думал, что интерфейсы существуют также для обеспечения дизайна класса (который включает в себя общедоступных, защищенных, частных членов и т. Д.), И все же чувствую, что должно быть что-то чтобы обеспечить его соблюдение. - person nawfal; 24.09.2012
comment
Поздний ответ, но представьте себе частных членов в общедоступном интерфейсе. Как класс мог бы реализовать все члены интерфейса, если бы он даже не знал, что они там есть? Конечно аналогично другим модификаторам. Как указано в принятом ответе, вы можете ограничить доступ к самому интерфейсу, предотвращая любые попытки даже реализовать контракт, когда вы не должны. Так сказать, частный / защищенный / внутренний интерфейс будет похож на контракт, о котором другие не знают. Взаимодействие с непубличными участниками будет похоже на нечеткий контракт, который невозможно полностью реализовать. - person user3613916; 27.06.2014

Я знаком с Java больше, чем с C #, но зачем вам частный член внутри интерфейса? У него не могло быть никакой реализации, и он был бы невидим для реализации классов, поэтому был бы бесполезен. Существуют интерфейсы, определяющие поведение. Если вам нужно поведение по умолчанию, используйте абстрактный класс.

person John Topley    schedule 20.08.2008
comment
внутренний член имеет смысл, не так ли? - person nawfal; 24.09.2012
comment
Я думаю, что можно объявить весь интерфейс (в отличие от отдельного метода) внутренним. В этом больше смысла. - person Jon Limjap; 24.09.2012
comment
@JonLimjap это возможно, и единственное, что он дает, это то, что он скрывает интерфейс от внешних сборок, как и ожидалось, но они по-прежнему требуют, чтобы его члены были общедоступными (что странно), что означает, что эти общедоступные свойства и методы видны внешнему миру! ! - person nawfal; 24.09.2012

На мой взгляд, это нарушает инкапсуляцию. Я должен реализовать методы как общедоступные, затем я реализую интерфейс. Я не вижу причин навязывать публике класс, реализующий интерфейс. (c #)

person Ini    schedule 13.03.2017