11 ответов:
Если вы реализуете два интерфейса, как с тем же методом, так и с разными реализациями, то вы должны реализовать явно.
public interface IDoItFast { void Go(); } public interface IDoItSlow { void Go(); } public class JustDoIt : IDoItFast, IDoItSlow { void IDoItFast.Go() { } void IDoItSlow.Go() { } }
полезно скрыть непривлекательный элемент. Например, если вы реализуете оба
IComparable<T>иIComparableобычно лучше скрытьIComparableперегрузка, чтобы не создавать у людей впечатление, что вы можете сравнивать объекты разных типов. Аналогично, некоторые интерфейсы не являются CLS-совместимыми, напримерIConvertible, поэтому, если вы явно не реализуете интерфейс, конечные пользователи языков, требующих соответствия CLS, не могут использовать ваш объект. (Что было бы очень катастрофично, если бы разработчики BCL сделали это не скрывайте IConvertible члены примитивов :))еще одно интересное замечание заключается в том, что обычно использование такой конструкции означает, что структура, которая явно реализует интерфейс, может вызывать их только путем бокса к типу интерфейса. Вы можете обойти это с помощью общих ограничений::
void SomeMethod<T>(T obj) where T:IConvertibleне будет вставлять int, когда вы передаете его ему.
некоторые дополнительные причины для реализации интерфейса явно:
обратной совместимости: в случае
ICloneableизменения интерфейса, реализующие члены класса метода не должны изменять свои сигнатуры метода.чище код: будет ошибка компилятора, если
Cloneметод удаляется из ICloneable, однако если вы реализуете метод неявно, вы можете получить неиспользуемый "осиротевший" public методыстрогой типизации: Чтобы проиллюстрировать историю supercat на примере, это был бы мой предпочтительный пример кода, реализующий
ICloneableразрешаетClone()чтобы быть строго типизированным, когда вы вызываете его непосредственно какMyObjectэкземпляр элемента:public class MyObject : ICloneable { public MyObject Clone() { // my cloning logic; } object ICloneable.Clone() { return this.Clone(); } }
еще один полезный метод, чтобы реализация функции метода возвращает значение, которое является более конкретным, чем указано в интерфейсе.
например, объект может реализовать ICloneable, но все еще имеет свой публично видимый метод клонирования, возвращающий свой собственный тип.
аналогично, IAutomobileFactory может иметь метод производства, который возвращает автомобиль, но FordExplorerFactory, который реализует IAutomobileFactory, может иметь свой Метод изготовления возвращает FordExplorer (который происходит от автомобиля). Код, который знает, что у него есть FordExplorerFactory, может использовать свойства FordExplorer для объекта, возвращаемого FordExplorerFactory, без необходимости типизации, в то время как код, который просто знал, что у него есть какой-то тип IAutomobileFactory, просто будет иметь дело с его возвращением как автомобиль.
Это также полезно, когда у вас есть два интерфейса с одинаковым именем и подписью, но хотите изменить поведение его в зависимости от того, как он используется. (Я не рекомендую писать такой код):
interface Cat { string Name {get;} } interface Dog { string Name{get;} } public class Animal : Cat, Dog { string Cat.Name { get { return "Cat"; } } string Dog.Name { get { return "Dog"; } } } static void Main(string[] args) { Animal animal = new Animal(); Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use. Dog dog = animal; Console.WriteLine(cat.Name); //Prints Cat Console.WriteLine(dog.Name); //Prints Dog }
Если у вас есть внутренний интерфейс, и вы не хотите, чтобы реализовать члены на вашем классе публично, вы бы реализовать их явно. Неявные реализации должны быть общедоступными.
Он может держать открытый интерфейс чище явно реализовать интерфейс, т. е. ваш
Fileкласс может реализоватьIDisposableявно и предоставить открытый методClose()что может сделать больше смысла для потребителя, чемDispose().F# только предлагает явную реализацию интерфейса, так что вы всегда должны привести к конкретному интерфейсу, чтобы получить доступ к его функциональности, что делает для очень явного (Не каламбур) использования интерфейса.
еще одна причина для явной реализации для ремонтопригодность.
когда класс становится "занятым" - да, это происходит, у нас не все имеют роскошь рефакторинга кода других членов команды-тогда наличие явной реализации дает понять, что метод находится там, чтобы удовлетворить контракт интерфейса.
таким образом, это улучшает "читаемость"кода.
в случае явно определенных интерфейсов все методы автоматически являются частными, вы не можете предоставить им доступ к модификатору public. Предположим:
interface Iphone{ void Money(); } interface Ipen{ void Price(); } class Demo : Iphone, Ipen{ void Iphone.Money(){ //it is private you can't give public Console.WriteLine("You have no money"); } void Ipen.Price(){ //it is private you can't give public Console.WriteLine("You have to paid 3$"); } } // So you have to cast to call the method class Program { static void Main(string[] args) { Demo d = new Demo(); Iphone i1 = (Iphone)d; i1.Money(); ((Ipen)i1).Price(); Console.ReadKey(); } } // You can't call methods by direct class object
вот как мы можем создать явный интерфейс: Если у нас есть 2 интерфейса, и оба интерфейса имеют один и тот же метод, и один класс наследует эти 2 интерфейса, поэтому при вызове одного метода интерфейса компилятор запутался, какой метод должен быть вызван, поэтому мы можем управлять этой проблемой с помощью явного интерфейса. Вот один пример, который я привел ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace oops3 { interface I5 { void getdata(); } interface I6 { void getdata(); } class MyClass:I5,I6 { void I5.getdata() { Console.WriteLine("I5 getdata called"); } void I6.getdata() { Console.WriteLine("I6 getdata called"); } static void Main(string[] args) { MyClass obj = new MyClass(); ((I5)obj).getdata(); Console.ReadLine(); } } }
другой пример дает
System.Collections.Immutable, в котором авторы решили использовать метод для сохранения знакомого API для типов коллекций, соскабливая части интерфейса, которые не имеют смысла для их новых типов.конкретно
ImmutableList<T>осуществляетIList<T>и такICollection<T>(в целях разрешитьImmutableList<T>для более легкого использования с устаревшим кодом), ноvoid ICollection<T>.Add(T item)не имеет смысла дляImmutableList<T>: после добавления элемента в неизменяемый список не должен изменять существующий список,ImmutableList<T>происходит отIImmutableList<T>чейIImmutableList<T> Add(T item)может использоваться для неизменяемых списков.таким образом, в случае
Addреализации вImmutableList<T>в конечном итоге выглядит следующим образом:public ImmutableList<T> Add(T item) { // Create a new list with the added item } IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value); void ICollection<T>.Add(T item) => throw new NotSupportedException(); int IList.Add(object value) => throw new NotSupportedException();
Comments