2010-11-05 4 views

ответ

132

Если вы реализуете два интерфейса, как с одним и тем же методом, так и с различными реализациями, вам необходимо реализовать явно.

public interface IDoItFast 
{ 
    void Go(); 
} 
public interface IDoItSlow 
{ 
    void Go(); 
} 
public class JustDoIt : IDoItFast, IDoItSlow 
{ 
    void IDoItFast.Go() 
    { 
    } 

    void IDoItSlow.Go() 
    { 
    } 
} 
+0

Да точно это один случай, который решает EIMI. И другие вопросы покрываются ответом Майкла Б. – crypted

+9

Отличный пример. Любите имена интерфейса/класса!:-) –

+8

Мне это не нравится, два метода с одной и той же подписью в классе, которые делают разные вещи? Это чрезвычайно опасный материал и, скорее всего, приведет к хаосу в любом крупном развитии. Если у вас есть такой код, я бы сказал, что ваш анализ и дизайн - это wahzoo. – Mick

7

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

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 
} 
+55

страннее OO родственный пример я когда-либо видел: 'класс животные общественности: Cat, Dog' – mbx

+36

@mbx: Если животное также реализована Parrot, это будет Polly морфинга животным. – RenniePet

+3

Я помню, мультипликационный персонаж, который был кот на одном конце и собака на другом конце ;-) –

5

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

+0

Хорошо, это объясняет, почему проект не будет компилироваться с неявной реализацией. – pauloya

5

Он может держать уборщик публичного интерфейса явно реализовать интерфейс, то есть ваш File класса может реализовать IDisposable явно и обеспечить публичный метод Close(), который мог бы сделать больше смысла для потребителя, чем Dispose().

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

+0

Я думаю, что большинство версий VB также поддерживали только явные определения интерфейсов. – Gabe

+1

@Gabe - это более тонко, чем для VB. Именование и доступность членов, реализующих интерфейс, отделены от указания того, что они являются частью реализации. Итак, в VB и глядя на ответ @ Iain (текущий верхний ответ), вы можете реализовать IDoItFast и IDoItSlow с публичными членами «GoFast» и «GoSlow» соответственно. –

+2

Мне не нравится ваш конкретный пример (IMHO, единственное, что должно скрыть «Dispose» - это те, которые никогда не потребуют очистки); лучший пример - это что-то вроде реализации неизменной коллекции 'IList .Add'. – supercat

60

Полезно скрыть нежелательный элемент. Например, если вы реализуете как IComparable<T>, так и IComparable, лучше всего скрыть перегрузку IComparable, чтобы не дать людям впечатление, что вы можете сравнивать объекты разных типов. Аналогично, некоторые интерфейсы не совместимы с CLS, например IConvertible, поэтому, если вы явно не реализуете интерфейс, конечные пользователи языков, которым требуется соответствие CLS, не могут использовать ваш объект. (Что было бы очень катастрофически, если бы исполнители BCL не скрывали IConvertible членов примитивов :))

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

void SomeMethod<T>(T obj) where T:IConvertible 

Не будет ящика в случае, если вы передадите его одному.

+1

У вас есть опечатка в ваших ограничениях. Чтобы выполнить клише, приведенный выше код работает. Он должен быть в начальном объявлении сигнатуры метода в интерфейсе. В оригинальной записи это не указано. Кроме того, правильный формат «недействительным SomeMehtod (Т OBJ), где Т:. IConvertible Обратите внимание, есть дополнительный толстой кишки между„)“и„где“, которые не должны быть там до сих пор, +1 для разумного использования воспроизведенных в. избежать дорогой бокс –

+1

Привет Michael B. Так почему в реализации строки в .NET есть общественная реализация IComparable: общественности INT CompareTo (значение объекта) { если (значение == NULL) { возврата 1; }. если { бросить новый ArgumentException (Environment.GetResourceString ("Arg_MustBeString")); } ((значение String)!) возвращение String.Compare (это, значение (String), StringComparison.CurrentCulture); } Спасибо! – zzfima

+0

'string' был вокруг вокруг дженериков, и эта практика была в моде. Когда появился .net 2, они не захотели нарушить открытый интерфейс «string», поэтому они оставили его, как это было с защитой на месте. –

13

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

Например, объект может реализовать ICloneable, но все же его общедоступный метод Clone возвращает свой собственный тип.

Аналогичным образом, у IAutomobileFactory может быть метод изготовления, который возвращает автомобиль, но у FordExplorerFactory, который реализует IAutomobileFactory, может быть, что метод его изготовления возвращает FordExplorer (который происходит от Automobile). Код, который знает, что у него есть FordExplorerFactory, может использовать специальные свойства FordExplorer на объекте, возвращенном FordExplorerFactory, без необходимости приведения типов, тогда как код, который просто знал, что у него есть какой-то тип IAutomobileFactory, просто будет иметь дело с его возвратом в качестве Автомобиля.

+2

+1 ... это было бы моим предпочтительным использованием явных реализаций интерфейса, хотя образец небольшого кода, вероятно, был бы более понятным, чем эта история :) –

+3

2 человека становятся слепыми, читая этот ответ. –

36

Некоторые дополнительные причины для реализации интерфейса в явном виде:

назад совместимость: В случае ICloneable изменения интерфейса, реализующие члены класса метода не должны изменять свои подписи метода.

чистый код: будет ошибкой компиляции, если метод Clone удаляется из ICloneable, однако, если вы реализуете метод неявно вы можете закончить с неиспользуемыми бесхозными публичными методами

Строгих типизацией : чтобы проиллюстрировать историю SUPERCAT с примером, это будет мой предпочтительный пример кода, внедрение ICloneable явно позволяет Clone() быть сильно типизированных при вызове его непосредственно в качестве члена в MyObject например:

public class MyObject : ICloneable 
{ 
    public MyObject Clone() 
    { 
    // my cloning logic; 
    } 

    object ICloneable.Clone() 
    { 
    return this.Clone(); 
    } 
} 
+0

Для этого я бы предпочел интерфейс ICloneable {T Clone(); T self {get;}} '. Обратите внимание, что намеренно не '' ICloneable ограничение на T. В то время как объект обычно может только быть безопасно клонированы, если его основание может быть, один, возможно, пожелает получить от базового класса, который можно было бы безопасно клонированного объект класса, который может «т. Чтобы это разрешить, я бы рекомендовал, чтобы наследуемые классы подвергали публичному методу клонирования. Вместо этого есть наследуемые классы с методом «защищенного» клонирования и запечатанные классы, которые вытекают из них и выставляют публичное клонирование. – supercat

+0

Уверен, что было бы лучше, если не было ковариантной версии ICloneable в BCL, так что вам нужно было бы создать ее правильно? –

4

Другая причина для явной реализации - ремонтопригодность.

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

Таким образом, он улучшает «читаемость» кода.

+0

IMHO более важно решить, должен ли класс быть или не должен подвергать метод своим клиентам. Это заставляет быть явным или неявным.Чтобы документировать, что несколько методов принадлежат друг другу, в этом случае, поскольку они удовлетворяют контракту - для чего используется '# region', с соответствующей строкой заголовка. И комментарий к методу. – ToolmakerSteve

0

Это, как мы можем создать Явный интерфейс: Если мы имеем 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();  
     } 
    } 
} 
0

В случае явно определенных интерфейсов, все методы автоматически частные, вы не можете дать доступ общественности модификатор к ним. Предположу:

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 
+0

«все методы автоматически закрыты» - это не является технически корректным, b/c, если они были на самом деле закрыты, тогда они вообще не будут вызываться, кастинг или нет. – samosaris

0

Другой пример даются System.Collections.Immutable, в которых авторы решили использовать технику, чтобы сохранить привычный интерфейс для типов коллекций, а скребли части интерфейса, которые не несут никакого смысла для их новых типов ,

В частности, ImmutableList<T> реализует IList<T> и, таким образом, ICollection<T> (in order, чтобы позволить 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(); 
Смежные вопросы