Зачем реализовывать интерфейс явно? - PullRequest
118 голосов
/ 05 ноября 2010

Итак, что же является хорошим вариантом использования для явной реализации интерфейса?

Только чтобы люди, использующие класс, не смотрели на все эти методы / свойства в intellisense?

Ответы [ 11 ]

139 голосов
/ 05 ноября 2010

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

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

    void IDoItSlow.Go()
    {
    }
}
63 голосов
/ 05 ноября 2010

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

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

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

Не будет вставлять int, когда вы передаете его ему.

36 голосов
/ 16 августа 2011

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

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

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

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

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

  object ICloneable.Clone()
  {
    return this.Clone();
  }
}
12 голосов
/ 05 ноября 2010

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

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

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

7 голосов
/ 05 ноября 2010

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

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
}
5 голосов
/ 05 ноября 2010

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

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

5 голосов
/ 05 ноября 2010

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

4 голосов
/ 26 июля 2016

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

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

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

1 голос
/ 01 октября 2017

Другой пример приведен 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();
0 голосов
/ 02 января 2017

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...