Каковы различия в реализации интерфейсов неявно и явно в C #?
Когда вы должны использовать неявное и когда вы должны использовать явное?
Есть ли плюсы и / или минусы одного или другого?
Официальные рекомендации Microsoft (из первой редакции Framework Design Guidelines ) гласят, что использование явных реализаций не рекомендуется , поскольку это приводит к неожиданному поведению кода.
Я думаю, что это руководство очень актуально в период до IoC , когда вы не передаете вещи как интерфейсы.
Может ли кто-нибудь затронуть и этот аспект?
Ответы:
Неявный - это когда вы определяете свой интерфейс через члена вашего класса. Явное - это когда вы определяете методы в вашем классе на интерфейсе. Я знаю, что это звучит странно, но вот что я имею в виду:
IList.CopyTo
было бы неявно реализовано как:и явно как:
Разница в том, что неявная реализация позволяет вам получить доступ к интерфейсу через класс, который вы создали, приведя интерфейс к этому классу и к самому интерфейсу. Явная реализация позволяет получить доступ к интерфейсу только путем приведения его в качестве самого интерфейса.
Я использую явное в первую очередь для поддержания чистоты реализации, или когда мне нужны две реализации. Несмотря на это, я редко использую это.
Я уверен, что есть больше причин использовать / не использовать явные, которые будут публиковать другие.
См. Следующий пост в этой теме для отличных рассуждений за каждым.
источник
public
ключевое слово ... иначе вы получите ошибкуUISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }
.IEnumerator<T>.Current
,IEnumerable<T>.GetEnumerator()
иISet<T>.Add(T)
. Это упоминается в другом ответе .Неявным определением будет просто добавить методы / свойства и т. Д., Требуемые интерфейсом, непосредственно к классу в качестве открытых методов.
Явное определение заставляет участников открываться только тогда, когда вы работаете с интерфейсом напрямую, а не с базовой реализацией. Это предпочтительнее в большинстве случаев.
источник
Cat
действительноIEatable
нет возражений.В дополнение к уже предоставленным отличным ответам, в некоторых случаях ТРЕБУЕТСЯ явная реализация, чтобы компилятор мог выяснить, что требуется. Взгляните на
IEnumerable<T>
основной пример, который, скорее всего, встречается довольно часто.Вот пример:
Здесь
IEnumerable<string>
реализуетIEnumerable
, следовательно, нам тоже нужно. Но подождите, и универсальная, и обычная версии реализуют функции с одинаковой сигнатурой метода (C # игнорирует возвращаемый тип для этого). Это совершенно законно и хорошо. Как компилятор решает, какой использовать? Это вынуждает вас иметь самое большее одно неявное определение, тогда оно может разрешить все, что ему нужно.то есть.
PS: Небольшая часть косвенности в явном определении для IEnumerable работает, потому что внутри функции компилятор знает, что фактическим типом переменной является StringList, и именно так он разрешает вызов функции. Кажется, что накоплен небольшой факт для реализации некоторых уровней абстракции, некоторые из основных интерфейсов .NET.
источник
abstract
.По словам Джеффри Рихтера из CLR с помощью C #
( EIMI означает Е Xplicit Я nterface М еню Я mplementation)
Если вы используете ссылку на интерфейс, любая виртуальная цепочка может быть явно заменена EIMI в любом производном классе, и когда объект такого типа приводится к интерфейсу, ваша виртуальная цепочка игнорируется и вызывается явная реализация. Это все что угодно, кроме полиморфизма.
EIMI также можно использовать для того, чтобы скрыть не строго типизированные элементы интерфейса от реализаций базовых интерфейсов Framework, таких как IEnumerable <T>, чтобы ваш класс не предоставлял напрямую не строго типизированный метод, но синтаксически правильный.
источник
Причина № 1
Я склонен использовать явную реализацию интерфейса, когда я хочу препятствовать «программированию к реализации» ( Принципы проектирования из Шаблонов проектирования ).
Например, в веб-приложении на основе MVP :
Теперь другой класс (например, презентатор ) с меньшей вероятностью будет зависеть от реализации StandardNavigator и с большей вероятностью будет зависеть от интерфейса INavigator (поскольку реализация должна быть приведена к интерфейсу, чтобы использовать метод Redirect).
Причина № 2
Другая причина, по которой я мог бы пойти с явной реализацией интерфейса, состоит в том, чтобы поддерживать чистоту интерфейса класса «по умолчанию». Например, если бы я разрабатывал серверный элемент управления ASP.NET , мне могли бы потребоваться два интерфейса:
Простой пример следует. Это поле со списком, в котором перечислены клиенты. В этом примере разработчик веб-страницы не заинтересован в заполнении списка; вместо этого они просто хотят иметь возможность выбрать клиента по GUID или получить GUID выбранного клиента. Презентатор заполняет поле при загрузке первой страницы, и этот презентатор инкапсулируется элементом управления.
Докладчик заполняет источник данных, и разработчик веб-страницы никогда не должен знать о его существовании.
Но это не серебряная пушка
Я не рекомендовал бы всегда использовать явные реализации интерфейса. Это всего лишь два примера, где они могут быть полезны.
источник
В дополнение к другим уже указанным причинам, это ситуация, в которой класс реализует два разных интерфейса, которые имеют свойство / метод с одинаковыми именем и сигнатурой.
Этот код компилируется и выполняется нормально, но свойство Title является общим.
Ясно, что мы бы хотели, чтобы возвращаемое значение Title зависело от того, относились ли мы к Class1 как к Книге или к Человеку. Это когда мы можем использовать явный интерфейс.
Обратите внимание, что явные определения интерфейса выводятся как Public - и, следовательно, вы не можете явно объявить их как public (или иным образом).
Также обратите внимание, что у вас все еще может быть «общая» версия (как показано выше), но, хотя это возможно, существование такого свойства сомнительно. Возможно, его можно было бы использовать в качестве реализации Title по умолчанию, чтобы не пришлось изменять существующий код для преобразования Class1 в IBook или IPerson.
Если вы не определили «общий» (неявный) заголовок, потребители Class1 должны сначала явным образом привести экземпляры Class1 к IBook или IPerson - в противном случае код не будет компилироваться.
источник
Я использую явную реализацию интерфейса большую часть времени. Вот основные причины.
Рефакторинг безопаснее
При изменении интерфейса лучше, если компилятор может это проверить. Это сложнее с неявными реализациями.
На ум приходят два распространенных случая:
Добавление функции в интерфейс, где уже существует класс, реализующий этот интерфейс, с методом с той же сигнатурой, что и у нового . Это может привести к неожиданному поведению, и укусил меня несколько раз. Трудно "увидеть" при отладке, потому что эта функция, вероятно, не находится с другими методами интерфейса в файле (проблема самодокументирования, упомянутая ниже).
Удаление функции из интерфейса . Неявно реализованные методы будут внезапно мертвым кодом, но явно реализованные методы будут захвачены ошибкой компиляции. Даже если мертвый код хорошо хранить, я хочу, чтобы его просмотрели и продвигали.
К сожалению, в C # нет ключевого слова, которое заставляет нас помечать метод как неявную реализацию, поэтому компилятор может выполнять дополнительные проверки. Виртуальные методы не имеют ни одной из вышеперечисленных проблем из-за обязательного использования 'override' и 'new'.
Примечание: для фиксированных или редко меняющихся интерфейсов (как правило, из API поставщика) это не проблема. Однако для моих собственных интерфейсов я не могу предсказать, когда и как они изменятся.
Это самодокументирование
Если я увижу public bool Execute () в классе, потребуется дополнительная работа, чтобы выяснить, что это часть интерфейса. Кто-то, вероятно, должен будет прокомментировать это, говоря об этом, или поместить его в группу других реализаций интерфейса, все в рамках региона или группового комментария, говорящего «реализация ITask». Конечно, это работает, только если заголовок группы не за кадром.
Принимая во внимание, что 'bool ITask.Execute ()' понятен и однозначен.
Четкое разделение реализации интерфейса
Я думаю, что интерфейсы являются более «общедоступными», чем общедоступными методами, потому что они созданы так, чтобы показать лишь небольшую часть поверхности конкретного типа. Они сводят тип к возможности, поведению, набору признаков и т. Д. И в реализации, я думаю, полезно сохранить это разделение.
Когда я просматриваю код класса, когда сталкиваюсь с явными реализациями интерфейса, мой мозг переключается в режим «контракта кода». Часто эти реализации просто перенаправляют на другие методы, но иногда они будут выполнять дополнительную проверку состояния / параметра, преобразование входящих параметров для лучшего соответствия внутренним требованиям или даже перевод для целей управления версиями (то есть несколько поколений интерфейсов, все переходящие к общим реализациям).
(Я понимаю, что public также являются контрактами на код, но интерфейсы намного сильнее, особенно в кодовой базе, управляемой интерфейсом, где прямое использование конкретных типов обычно является признаком кода, предназначенного только для внутреннего использования.)
Связанный: Причина 2 выше Джон .
И так далее
Плюс преимущества, уже упомянутые в других ответах здесь:
Проблемы
Это не все веселье и счастье. В некоторых случаях я придерживаюсь следствий:
Кроме того, может быть затруднительно выполнять приведение, когда у вас действительно есть конкретный тип и вы хотите вызвать явный интерфейсный метод. Я имею дело с этим одним из двух способов:
public IMyInterface I { get { return this; } }
(который должен быть встроен) и вызовитеfoo.I.InterfaceMethod()
. Если несколько интерфейсов нуждаются в этой способности, расширьте имя за пределы I (по моему опыту, это редко случается).источник
Если вы реализуете явно, вы сможете ссылаться на элементы интерфейса только через ссылку, которая имеет тип интерфейса. Ссылка, являющаяся типом реализующего класса, не будет предоставлять эти члены интерфейса.
Если ваш реализующий класс не является общедоступным, за исключением метода, используемого для создания класса (который может быть фабрикой или контейнером IoC ), и за исключением методов интерфейса (конечно), то я не вижу никакого преимущества в явной реализации интерфейсы.
В противном случае явная реализация интерфейсов гарантирует, что ссылки на ваш конкретный реализующий класс не будут использоваться, что позволит вам изменить эту реализацию позже. «Уверен», я полагаю, является «преимуществом». Хорошо реализованная реализация может выполнить это без явной реализации.
Недостаток, на мой взгляд, заключается в том, что вы найдете приведение типов к / из интерфейса в коде реализации, который имеет доступ к закрытым членам.
Как и многие вещи, преимущество является недостатком (и наоборот). Явно реализующие интерфейсы гарантируют, что ваш код реализации конкретного класса не будет представлен.
источник
Неявная реализация интерфейса - это когда у вас есть метод с такой же сигнатурой интерфейса.
Явная реализация интерфейса - это то, где вы явно объявляете, к какому интерфейсу принадлежит метод.
MSDN: неявные и явные реализации интерфейса
источник
Каждый член класса, который реализует интерфейс, экспортирует объявление, которое семантически похоже на способ написания объявлений интерфейса VB.NET, например
Хотя имя члена класса часто совпадает с именем члена интерфейса, и член класса часто будет общедоступным, ни одна из этих вещей не требуется. Можно также заявить:
В этом случае классу и его производным будет разрешен доступ к члену класса с использованием имени
IFoo_Foo
, но внешний мир сможет получить доступ к этому конкретному члену только путем приведения к немуIFoo
. Такой подход часто хорош в тех случаях, когда интерфейсный метод будет иметь определенное поведение во всех реализациях, но полезное поведение только в некоторых [например, заданное поведение дляIList<T>.Add
метода коллекции только для чтения - выброситьNotSupportedException
]. К сожалению, единственный правильный способ реализации интерфейса в C #:Не так приятно.
источник
Одним из важных применений явной реализации интерфейса является необходимость реализации интерфейсов со смешанной видимостью. .
Проблема и решение хорошо объяснены в статье C # Внутренний интерфейс .
Например, если вы хотите защитить утечку объектов между прикладными уровнями, этот метод позволяет указать различную видимость элементов, которые могут вызвать утечку.
источник
Предыдущие ответы объясняют, почему реализация интерфейса явно в C # может быть предпочтительнее (в основном по формальным причинам). Однако есть одна ситуация, где явная реализация обязательна : во избежание утечки инкапсуляции, когда интерфейс не является
public
, но реализующий класс естьpublic
.Вышеуказанная утечка неизбежна, потому что, согласно спецификации C # , «все члены интерфейса неявно имеют открытый доступ». Как следствие, неявные реализации также должны предоставлять
public
доступ, даже если сам интерфейс, например,internal
.Неявная реализация интерфейса в C # - большое удобство. На практике многие программисты используют его постоянно / везде без дальнейшего рассмотрения. Это приводит к грязным поверхностям в лучшем случае и к утечке в худшем случае. Другие языки, такие как F #, даже не позволяют этого .
источник