Каковы веские причины для использования явной реализации интерфейса с единственной целью скрыть участников?


11

Во время одного из моих исследований тонкостей C # я наткнулся на интересный отрывок, касающийся явной реализации интерфейса.

While this syntax is quite helpful when you need to resolve name clashes, you can use explicit interface implementation simply to hide more "advanced" members from the object level.

Разница между разрешением использования object.method()или требованием применения заклинания ((Interface)object).method()кажется мне неопытным злым умыслом. Текст отметил , что это будет скрывать метод от Intellisense на уровне объекта, но почему вы хотите сделать это , если это не было необходимо , чтобы избежать конфликтов имен?


Ответы:


12

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

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


6
Это только я, или это звучит как ужасная идея?
Кевин Пено

5
@ Кевин, как так? Иногда интерфейс находится вне вашего контроля, и вы должны использовать его. Смягчение ущерба властного интерфейса кажется разумным.
Крис Питман

1
+1 за указание на то, что реальный мир много раз мешает правильному образу действий. @Kevin да это ужасная идея , но иногда у вас нет выбора и приходится работать с отстойных - лучше , чтобы скрыть ее и сделать фасад делать вещи правильно , когда вы не можете действительно сделать это правильно.
Уэйн Молина

@ Уэйн, я понимаю твои аргументы в пользу того, чтобы хотеть / использовать такую ​​систему. Черт, я бы, наверное, использовал это именно в том случае, если ты это утверждаешь. Я предполагаю, что мой короткий комментарий на самом деле просто указывал на то, что это неправильно, так зачем разрешать это на языке.
Кевин Пено

1
Чрезмерное разделение интерфейсов может быть основным препятствием для компоновки и агрегирования. Рассмотрим, например, что один хочет иметь реализацию, IEnumerable<T>которая ведет себя как объединение двух других. Если IEnumerable<T>включено свойство, позволяющее определить, было ли его число известным, потенциально бесконечным или нет, вместе с методом для запроса значения счетчика, класс, который объединяет несколько IEnumerable<T>и ведет себя как конкатенация, мог бы эффективно сообщать свой счет в том случае, если некоторые из инкапсулированные коллекции знали свое значение.
суперкат

4

Самым полезным приложением, которое я нашел, является реализация фабрик. Во многих случаях полезно создавать классы, которые являются изменяемыми внутри фабрики, но неизменяемыми для внешних классов. Это может быть легко реализовано в Java с использованием внутренних классов, делая поля и их установщики частными и выставляя получатели только как открытые члены. Однако в C # мне пришлось использовать явные интерфейсы для достижения того же. Я объясню дальше:

В Java внутренний класс И внешний класс могут обращаться к закрытым членам друг друга, что имеет смысл, поскольку классы очень тесно связаны. Они находятся в одном файле кода и, вероятно, разработаны одним и тем же разработчиком. Это означает, что фабрика все еще может получить доступ к закрытым полям и методам внутреннего класса, чтобы изменить их значения. Но внешние классы не смогут получить доступ к этим полям, кроме как через своих общедоступных получателей.

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

Пример:

public class Factory
{
    // factory method to create a hard-coded Mazda Tribute car.
    public static Car CreateCar()
    {
        Car car = new Car();

        // the Factory class can modify the model because it has access to
        // the private ICarSetters interface
        ((ICarSetters)car).model = "Mazda Tribute";

        return car;
    }

    // define a private interface containing the setters.
    private interface ICarSetters
    {
        // define the setter in the private interface
        string model { set; }
    }

    // This is the inner class. It has a member "model" that should not be modified
    // but clients, but should be modified by the factory.
    public class Car: ICarSetters
    {
        // explicitly implement the setter
        string ICarSetters.model { set; }

        // create a public getter
        public string model { get; }
    }
}

class Client
{
    public Client()
    {
        Factory.Car car = Factory.CreateCar();

        // can only read model because only the getter is public
        // and ICarSetters is private to Factory
        string model = car.model;
    }
}

Вот для чего я бы использовал явные интерфейсы.


3

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

class Test 
{
    static void Main()

    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

Напротив, явная реализация одного (или обоих) из них позволяет вам определять различное поведение для каждого.


1

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

Например, объект, который поддерживает коллекцию дочерних объектов, которые должны взаимодействовать с родителем.

Есть некоторые действия, которые мы хотим сделать доступными только для внешних абонентов, и некоторые действия, которые мы хотим сделать доступными только для дочерних объектов.

Явные интерфейсы помогают обеспечить это разделение.

    static void Main(string[] args)
    {
        var parent = new Parent();
        parent.DoExternalStuff();
    }

    interface IExternal {
        void DoExternalStuff();
    }

    interface IInternal {
        void DoInternalStuff();
    }

    class Parent : IExternal, IInternal
    {
        public Parent()
        {
            var child = new Child(this);
        }

        public void DoExternalStuff()
        {
           // do something exciting here for external callers
        }

        void IInternal.DoInternalStuff()
        {
            // do something exciting here for internal callers
        }
    }

    class Child
    {
        public Child(IInternal parent)
        {
            parent.DoInternalStuff();
        }
    }

0

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

Почему вы делаете это, а не предоставляете, скажем, второй «интерфейс расширенных возможностей», я не знаю.


0

Код обычно читается больше, чем написано, и я использую только Intellisense для быстрого написания кода. Я бы не стал писать код определенным образом, чтобы сделать Intellisense лучше.

Я не особо понимаю, как

((Интерфейс) объект) .method () более читабелен, чем object.method ().

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.