Интерфейсы C #. Неявная реализация против явной реализации

В чем разница в реализации интерфейсов неявно и явно в C #?

Когда следует использовать неявное, а когда явное?

Есть ли у того или другого какие-то плюсы и / или минусы?


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

Я думаю, что это руководство очень справедливо в эпоху, предшествующую IoC, когда вы не передаете вещи как интерфейсы.

Может ли кто-нибудь коснуться и этого аспекта?


person Seb Nilsson    schedule 27.09.2008    source источник
comment
Полная статья об интерфейсах C #: planetofcoders.com/c-interfaces   -  person Gaurav Agrawal    schedule 17.05.2012
comment
Да, явных интерфейсов следует избегать, и для реализации ISP (принцип разделения интерфейсов) следует использовать более профессиональный подход. Вот подробная статья о том же codeproject.com/Articles/1000374/   -  person Shivprasad Koirala    schedule 15.06.2015


Ответы (12)


Неявный - это когда вы определяете свой интерфейс через член своего класса. Явный - это когда вы определяете методы в своем классе в интерфейсе. Я знаю, что это звучит запутанно, но я имею в виду следующее: IList.CopyTo будет неявно реализован как:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

и явно как:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

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

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

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

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

См. следующее сообщение в этой теме. за отличное обоснование каждого.

person mattlant    schedule 27.09.2008
comment
Я знаю, что этот пост старый, но я нашел его очень полезным - если он непонятен, потому что не для меня, это то, что в этом примере неявный содержит ключевое слово public ... в противном случае вы получите ошибка - person jharr100; 18.11.2014
comment
CLR Джеффри Рихтера через C # 4 ed ch 13 показывает пример, в котором приведение типов не требуется: внутренняя структура SomeValueType: IComparable {private Int32 m_x; общедоступный SomeValueType (Int32 x) {m_x = x; } public Int32 CompareTo (SomeValueType другой) {...);} Int32 IComparable.CompareTo (Объект другой) {return CompareTo ((SomeValueType) другой); }} public static void Main () {SomeValueType v = new SomeValueType (0); Объект o = новый объект (); Int32 n = v.CompareTo (v); // Без бокса n = v.CompareTo (o); // ошибка времени компиляции} - person Andy Dent; 26.08.2015
comment
В любом случае имеет смысл иметь обе реализации, неявную и явную? - person Raskolnikov; 17.12.2015
comment
Сегодня я столкнулся с редкой ситуацией, которая ТРЕБУЕТ использования явного интерфейса: класс с полем, сгенерированным построителем интерфейса, который создает это поле как частное (Xamarin нацелен на iOS, с использованием раскадровки iOS). И интерфейс, в котором имеет смысл раскрыть это поле (общедоступное только для чтения). Я мог бы изменить имя получателя в интерфейсе, но существующее имя было наиболее логичным именем для объекта. Поэтому вместо этого я сделал явную реализацию, относящуюся к частному полю: UISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }. - person ToolmakerSteve; 13.07.2016
comment
Ужасы программирования. Что ж, красиво развенчано! - person Liquid Core; 03.05.2018
comment
@ToolmakerSteve другая ситуация (довольно распространенная), которая требует явной реализации хотя бы одного члена интерфейса, реализует несколько интерфейсов, которые имеют члены с одинаковой сигнатурой, но с разными типами возвращаемых значений. Это может произойти из-за наследования интерфейсов, как это происходит с IEnumerator<T>.Current, IEnumerable<T>.GetEnumerator() и ISet<T>.Add(T). Об этом упоминается в другом ответе. - person phoog; 21.05.2020

Неявное определение заключалось бы в том, чтобы просто добавить методы / свойства и т. Д., Требуемые интерфейсом, непосредственно в класс в качестве общедоступных методов.

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

  1. Работая напрямую с интерфейсом, вы не подтверждаете и не связываете свой код с базовой реализацией.
  2. Если у вас уже есть, скажем, общедоступное свойство Name в вашем коде и вы хотите реализовать интерфейс, который также имеет свойство Name, при явном выполнении эти два свойства будут разделены. Даже если бы они делали то же самое, я бы все равно делегировал явный вызов свойству Name. Вы никогда не знаете, вы можете захотеть изменить то, как Name работает для обычного класса, и как Name, свойство интерфейса работает позже.
  3. Если вы реализуете интерфейс неявно, тогда ваш класс теперь демонстрирует новое поведение, которое может иметь отношение только к клиенту интерфейса, и это означает, что вы не сохраняете свои классы достаточно лаконичными (мое мнение).
person Phil Bennett    schedule 27.09.2008
comment
Вы сделали здесь несколько хороших выводов. особенно A. Я обычно передаю свои классы как интерфейс, но я никогда не думал об этом с этой точки зрения. - person mattlant; 27.09.2008
comment
Я не уверен, что согласен с пунктом C. Объект Cat может реализовывать IEatable, но Eat () - это основная часть этого. Могут быть случаи, когда вы захотите просто вызвать Eat () на Cat, когда вы используете «необработанный» объект, а не через интерфейс IEatable, не так ли? - person LegendLength; 06.05.2009
comment
Я знаю некоторые места, где Cat действительно IEatable без возражений. - person Humberto; 14.06.2010
comment
Я полностью не согласен со всем вышеперечисленным и сказал бы, что использование явных интерфейсов - это рецепт катастрофы, а не ООП или ООД по их определению (см. Мой ответ о полиморфизме) - person Valentin Kuzub; 09.02.2011
comment
@ Валентин Кузуб: Во-первых, я не нашел от вашего имени ответа по полиморфизму. Во-вторых, во многих случаях требуются явные реализации. С академической точки зрения у вас могут быть аргументы в пользу рациональности ООП. С точки зрения хорошо работающего кода, нельзя оспаривать необходимость этой реализации в правильном контексте. Я столкнулся с этим в первый раз, когда попытался создать многоуровневое приложение с произвольным подключением. Возможность передавать данные через интерфейсы имела решающее значение, однако предотвращение отрицательного преобразования и конфликта имен в заводской модели предотвратило истинное разделение. - person Zack Jannsen; 18.09.2012
comment
См. Также: stackoverflow.com/questions/4103300 / - person Zack Jannsen; 18.09.2012
comment
Несколько лет спустя для комментариев, но в любом случае: если у вас есть явная реализация, делать что-либо, кроме указания на неявную реализацию, это проблематично. Допустим, у вас есть class MyClass : IInterface, где IInterface требует string Name { get; }, а у вас есть экземпляр MyClass my = new MyClass();. С вашим предложением my.Name вернет что-то отличное от ((IInterface)my).Name, что является нарушением принципа замены Лискова. И это не только академическое, но и может обернуться ошибкой, которую очень сложно найти. - person UweB; 10.01.2020
comment
-1 Явное определение ... Это предпочтительнее в большинстве случаев. Это вопрос мнения. Я не согласен. (Я занимаюсь объектно-ориентированным дизайном с рабочих станций Xerox Dandelion в начале 80-х, и с тех пор использую широкий спектр языков; в течение этих десятилетий я активно изучал объектно-ориентированный дизайн.) - person ToolmakerSteve; 22.05.2020
comment
Пункт 3 очень убедителен. недостаточная краткость ваших классов (мое мнение) может быть перефразировано как нарушение принципа разделения интерфейса SOLID - person Arialdo Martini; 06.12.2020

Помимо уже предоставленных отличных ответов, в некоторых случаях ТРЕБУЕТСЯ явная реализация, чтобы компилятор мог определить, что требуется. Взгляните на IEnumerable<T> как на яркий пример, который, вероятно, будет встречаться довольно часто.

Вот пример:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

Здесь IEnumerable<string> реализует IEnumerable, значит, нам тоже нужно. Но подождите, как общая, так и обычная версии обе реализуют функции с одной и той же сигнатурой метода (C # игнорирует тип возвращаемого значения для этого). Это совершенно законно и нормально. Как компилятор решает, что использовать? Он заставляет вас иметь только одно неявное определение, а затем может разрешить все, что ему нужно.

ie.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

PS: Небольшая косвенность в явном определении IEnumerable работает, потому что внутри функции компилятор знает, что фактический тип переменной - это StringList, и это то, как он разрешает вызов функции. Замечательный факт для реализации некоторых уровней абстракции, которые, похоже, накопились в некоторых интерфейсах ядра .NET.

person Matthew Scharley    schedule 27.09.2008
comment
@Tassadaque: Спустя 2 года все, что я могу сказать, это хороший вопрос. Понятия не имею, кроме, возможно, того, что я скопировал этот код из того, над чем работал, где он находился abstract. - person Matthew Scharley; 27.06.2011
comment
@Tassadaque, вы правы, но я думаю, что смысл сообщения Мэтью Шарли выше не утерян. - person funkymushroom; 07.07.2016

Процитируем Джеффри Рихтера из CLR через C #
(EIMI означает E явный I интерфейс M ethod I реализация)

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

  • Нет документации, объясняющей, как тип конкретно реализует метод EIMI, и нет Microsoft Visual Studio IntelliSense поддержка.
  • Экземпляры типа значения помещаются в рамку при приведении к интерфейсу.
  • EIMI не может быть вызван производным типом.

Если вы используете ссылку на интерфейс, ЛЮБАЯ виртуальная цепочка может быть явно заменена на EIMI в любом производном классе, и когда объект такого типа приводится к интерфейсу, ваша виртуальная цепочка игнорируется и вызывается явная реализация. Это что угодно, только не полиморфизм.

EIMI также можно использовать для скрытия нестрого типизированных элементов интерфейса от базовых реализаций интерфейсов Framework, таких как IEnumerable ‹T>, чтобы ваш класс не предоставлял не строго типизированный метод напрямую, но был синтаксически правильным.

person Valentin Kuzub    schedule 09.02.2011
comment
Повторная реализация интерфейсов, хотя и легальна, в лучшем случае, как правило, сомнительна. Явные реализации обычно должны быть связаны с виртуальным методом либо напрямую, либо через логику упаковки, которая должна быть привязана к производным классам. Хотя, безусловно, можно использовать интерфейсы способами, которые враждебны надлежащим соглашениям ООП, это не означает, что их нельзя использовать лучше. - person supercat; 31.05.2014
comment
@Valentin Что означают EIMI и IEMI? - person Dzienny; 09.06.2014
comment
Явная реализация метода интерфейса - person scobi; 15.08.2014
comment
Тогда должен IEMI не быть IIMI? - person gravidThoughts; 02.11.2014
comment
-1 за. Обычно я рассматриваю интерфейсы как полу (в лучшем случае) объектно-ориентированную функцию, она обеспечивает наследование, но не обеспечивает настоящего полиморфизма. Я категорически не согласен. Напротив, все интерфейсы - это полиморфизм, а не наследование. Они приписывают одному типу несколько классификаций. Избегайте IEMI, когда можете, и делегируйте, как предлагает @supercat, когда вы не можете. Не избегайте интерфейсов. - person Aluan Haddad; 04.02.2016
comment
EIMI не может быть вызван производным типом. << КАКИЕ? Это не правда. Если я явным образом реализую интерфейс для типа, то я получаю его от этого типа, я все равно могу привести его к интерфейсу для вызова метода, точно так же, как я должен был бы это сделать для типа, для которого он реализован. Так что не понимаю, о чем вы говорите. Даже внутри производного типа я могу просто привести его к рассматриваемому интерфейсу, чтобы получить доступ к явно реализованному методу. - person Triynko; 01.11.2018
comment
EIMI - это явная реализация члена интерфейса. Не все члены интерфейса являются методами; некоторые свойства. - person phoog; 21.05.2020

Причина # 1

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

Например, в сети на основе MVP заявление:

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

Теперь другой класс (например, ведущий) с меньшей вероятностью будет зависеть от реализации StandardNavigator и с большей вероятностью будет зависеть от интерфейса INavigator (поскольку реализация должна быть приведена к интерфейсу, чтобы использовать метод Redirect).

Причина # 2

Еще одна причина, по которой я мог бы пойти с явной реализацией интерфейса, - это сохранить чистоту интерфейса класса по умолчанию. Например, если бы я разрабатывал серверный элемент управления ASP.NET, мне могли бы понадобиться два интерфейса :

  1. Основной интерфейс класса, используемый разработчиками веб-страниц; а также
  2. Скрытый интерфейс, используемый докладчиком, который я разрабатываю для обработки логики элемента управления.

Вот простой пример. Это поле со списком, в котором перечислены клиенты. В этом примере разработчик веб-страницы не заинтересован в заполнении списка; вместо этого они просто хотят иметь возможность выбрать клиента по GUID или получить GUID выбранного клиента. Презентатор заполняет поле при загрузке первой страницы, и этот докладчик инкапсулируется элементом управления.

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

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

Но это не серебряное пушечное ядро

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

person Jon Nadal    schedule 14.06.2010

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

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

Этот код компилируется и работает нормально, но свойство Title является общим.

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

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

Обратите внимание, что явные определения интерфейсов считаются общедоступными, и, следовательно, вы не можете явно объявить их общедоступными (или иным образом).

Также обратите внимание, что у вас все еще может быть «общая» версия (как показано выше), но, хотя это возможно, существование такого свойства сомнительно. Возможно, его можно было бы использовать в качестве реализации Title по умолчанию, чтобы не пришлось изменять существующий код, чтобы преобразовать Class1 в IBook или IPerson.

Если вы не определяете «общий» (неявный) Заголовок, потребители Class1 должны сначала явно привести экземпляры Class1 к IBook или IPerson - в противном случае код не будет компилироваться.

person Lee Oades    schedule 01.10.2008

Большую часть времени я использую явную реализацию интерфейса. Вот основные причины.

Рефакторинг безопаснее

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

На ум приходят два распространенных случая:

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

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

К сожалению, в C # нет ключевого слова, которое заставляет нас помечать метод как неявную реализацию, чтобы компилятор мог выполнять дополнительные проверки. Виртуальные методы не имеют ни одной из вышеперечисленных проблем из-за обязательного использования 'override' и 'new'.

Примечание: для фиксированных или редко меняющихся интерфейсов (обычно из API поставщика) это не проблема. Однако для моих собственных интерфейсов я не могу предсказать, когда и как они изменятся.

Самодокументируется

Если я вижу «public bool Execute ()» в классе, потребуется дополнительная работа, чтобы выяснить, что это часть интерфейса. Кому-то, вероятно, придется прокомментировать это, сказав это, или поместить его в группу других реализаций интерфейса, и все это будет под областью или групповым комментарием, говорящим «реализация ITask». Конечно, это работает только в том случае, если заголовок группы не находится за кадром.

Тогда как: 'bool ITask.Execute ()' ясен и недвусмысленен.

Четкое разделение реализации интерфейса

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

Когда я просматриваю код класса, когда я сталкиваюсь с явными реализациями интерфейса, мой мозг переключается в режим «контракта кода». Часто эти реализации просто перенаправляют к другим методам, но иногда они будут выполнять дополнительную проверку состояния / параметров, преобразование входящих параметров для лучшего соответствия внутренним требованиям или даже перевод для целей управления версиями (то есть несколько поколений интерфейсов все переходят к общим реализациям).

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

Связано: Причина 2 выше, Джон.

И так далее

Плюс преимущества, уже упомянутые в других ответах здесь:

Проблемы

Это еще не все веселье и счастье. В некоторых случаях я придерживаюсь имплицитов:

  • Типы значений, потому что для этого потребуется упаковка и более низкая производительность. Это не строгое правило и зависит от интерфейса и того, как он предназначен для использования. Iomparable? Скрытый. IFormattable? Наверное, явно.
  • Тривиальные системные интерфейсы, у которых есть методы, которые часто вызываются напрямую (например, IDisposable.Dispose).

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

  1. Добавьте паблики и отправьте им методы интерфейса для реализации. Обычно это происходит с более простыми интерфейсами при внутренней работе.
  2. (Мой предпочтительный метод) Добавьте public IMyInterface I { get { return this; } } (который должен быть встроен) и вызовите foo.I.InterfaceMethod(). Если несколько интерфейсов нуждаются в этой возможности, расширьте имя за пределами I (по моему опыту, мне редко это нужно).
person scobi    schedule 15.08.2014

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

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

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

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

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

person Bill    schedule 27.09.2008
comment
Хороший ответ, Билл. Другие ответы были отличными, но вы предоставили некоторые дополнительные объективные точки зрения (помимо своего мнения), которые облегчили мне понимание. Как и у большинства вещей, у явных или неявных реализаций есть свои плюсы и минусы, поэтому вам просто нужно выбрать лучший вариант для вашего конкретного сценария или варианта использования. Я бы сказал, что тем, кто пытается лучше понять это, будет полезно прочитать ваш ответ. - person Daniel Eagle; 02.12.2014

Неявная реализация интерфейса - это когда у вас есть метод с той же сигнатурой интерфейса.

Явная реализация интерфейса - это когда вы явно объявляете, к какому интерфейсу принадлежит метод.

interface I1
{
    void implicitExample();
}

interface I2
{
    void explicitExample();
}


class C : I1, I2
{
    void implicitExample()
    {
        Console.WriteLine("I1.implicitExample()");
    }


    void I2.explicitExample()
    {
        Console.WriteLine("I2.explicitExample()");
    }
}

MSDN: неявные и явные реализации интерфейса

person Yochai Timmer    schedule 26.05.2011

Каждый член класса, реализующий интерфейс, экспортирует объявление, которое семантически похоже на способ написания объявлений интерфейса VB.NET, например

Public Overridable Function Foo() As Integer Implements IFoo.Foo

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

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

В этом случае классу и его производным будет разрешен доступ к члену класса, использующему имя IFoo_Foo, но внешний мир сможет получить доступ к этому конкретному члену только путем преобразования в IFoo. Такой подход часто бывает хорош в тех случаях, когда у метода интерфейса будет заданное поведение для всех реализаций, но полезное поведение только для некоторых [например, указанное поведение для метода IList<T>.Add коллекции только для чтения - выбросить NotSupportedException]. К сожалению, единственный правильный способ реализовать интерфейс на C #:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

Не так хорошо.

person supercat    schedule 12.05.2014

Предыдущие ответы объясняют, почему явная реализация интерфейса на C # может быть предпочтительной (в основном по формальным причинам). Однако есть одна ситуация, когда явная реализация является обязательной: чтобы избежать утечки инкапсуляции, когда интерфейс не-public, а класс реализации - public.

// Given:
internal interface I { void M(); }

// Then explicit implementation correctly observes encapsulation of I:
// Both ((I)CExplicit).M and CExplicit.M are accessible only internally.
public class CExplicit: I { void I.M() { } }

// However, implicit implementation breaks encapsulation of I, because
// ((I)CImplicit).M is only accessible internally, while CImplicit.M is accessible publicly.
public class CImplicit: I { public void M() { } }

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

Неявная реализация интерфейса на C # очень удобна. На практике многие программисты используют его все время / везде без дальнейших размышлений. Это приводит в лучшем случае к грязным поверхностям, а в худшем - к утечке инкапсуляции. Другие языки, например F #, даже не разрешать.

person Marc Sigrist    schedule 16.10.2019

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

Проблема и решение подробно описаны в статье Внутренний интерфейс C # .

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

person nrodic    schedule 03.11.2012