Итак, что же точно используется для реализации интерфейса явно?
Это только для того, чтобы люди, использующие класс, не должны были смотреть на все эти методы/свойства в intellisense?
Итак, что же точно используется для реализации интерфейса явно?
Это только для того, чтобы люди, использующие класс, не должны были смотреть на все эти методы/свойства в intellisense?
Если вы реализуете два интерфейса, как с одним и тем же методом, так и с различными реализациями, вы должны явно реализовать.
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, однако, если вы реализуете метод неявно, вы можете получить неиспользуемые общедоступные методы "осиротевших" p >
сильная типизация:
Чтобы проиллюстрировать пример суперкарта с примером, это будет мой предпочтительный примерный код, реализация ICloneable
явно позволяет строго указать t23, когда вы вызываете его непосредственно как член экземпляра MyObject
:
public class MyObject : ICloneable
{
public MyObject Clone()
{
// my cloning logic;
}
object ICloneable.Clone()
{
return this.Clone();
}
}
Еще один полезный метод заключается в том, чтобы функция публичной реализации метода возвращала значение, которое более конкретно, чем указано в интерфейсе.
Например, объект может реализовать ICloneable, но при этом его общедоступный метод Clone возвращает свой собственный тип.
Аналогично, у IAutomobileFactory может быть метод изготовления, который возвращает Автомобиль, но у FordExplorerFactory, который реализует IAutomobileFactory, может быть, что метод его изготовления возвращает FordExplorer (который происходит от Automobile). Код, который знает, что у него есть 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 # только предлагает явную реализацию интерфейса, поэтому вам всегда нужно использовать конкретный интерфейс для доступа к его функциям, что позволяет использовать явный (без использования каламбура) интерфейс.
Еще одна причина явной реализации - ремонтопригодность.
Когда класс становится "занятым" - да, это происходит, у нас не все есть роскошь рефакторинга кода других членов команды, а затем с явной реализацией дает понять, что существует метод для удовлетворения интерфейса.
Таким образом, он улучшает "читаемость" кода.
Вот как мы можем создать Явный интерфейс: Если у нас есть 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();
}
}
}
В случае явно определенных интерфейсов все методы автоматически закрыты, вы не можете публиковать им доступный модификатор. Пусть:
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
Другой пример представлен 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();