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

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

Ответов (9)

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

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

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

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

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

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

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

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

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

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

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

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

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

Вы можете скрыть реализацию интерфейса, явно указав имя интерфейса перед именем метода:

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
    }
}

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

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

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

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

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

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();
    }
}