Які є вагомі причини використовувати явну реалізацію інтерфейсу з єдиною метою приховування членів?


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
@Kevin, як так? Іноді інтерфейс поза вашим контролем, і вам доведеться ним користуватися. Пом'якшення пошкодження владного інтерфейсу здається розумним.
Кріс Пітман

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

@Wayne, я розумію ваші міркування щодо бажання / використання такої системи. Чорт, я, мабуть, використаю це саме в тому випадку, коли ви заявляєте. Я думаю, мій короткий коментар насправді просто вказував на те, що це неправильно, тому навіщо це допускати мовою.
Кевін Пено

1
Надмірне розділення інтерфейсу може бути головною перешкодою для складання та агрегації. Розглянемо, наприклад, що хочеться здійснити реалізацію, IEnumerable<T>яка поводиться як об'єднання двох інших. Якщо IEnumerable<T>включено властивість, щоб сказати, чи відомий його підрахунок, потенційно нескінченний, або жоден із них, поряд із методом запитання про те, який його підрахунок, клас, який агрегує множину IEnumerable<T>і поводиться як конкатенація, може ефективно повідомити про свій підрахунок у випадку, якщо хтось із інкапсульовані колекції знали їх кількість.
supercat

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.