Одна очень распространенная комбинация этих двух случаев - при реализации IEnumerable<T>
.Поскольку IEnumerable<T>
наследуется от IEnumerable
, то вы должны реализовать как IEnumerable<T>.GetEnumerator()
, так и IEnumerable.GetEnumerator()
.Так как они имеют другой тип возвращаемого значения, но совпадают со списком параметров (пустым), неявно может быть реализован только один не более.
Наиболее распространенный шаблон - реализация универсальной версии:
public IEnumerator<T> GetEnumerator()
{
//some code that returns an appropriate object.
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();//return the result of the other call.
}
Теперь не нужно иметь неявное, или можно (с небольшим приведением в явную реализацию) иметь это наоборот.Однако это удобнее по следующим причинам:
- Мы, вероятно, предпочли бы, чтобы неявная версия вызывалась, поскольку она, вероятно, будет иметь лучшую безопасность типов и, возможно, лучшую эффективность, если будет обработан объект перечислителя.через его более явный интерфейс.
- Неявная версия будет обслуживать как вызывающий код, который хочет
IEnumerator<T>
, так и вызывающий код, который хочет IEnumerator
(так как это неявное приведение), и, следовательно, является наиболее полезным.
Теперь это тот случай, когда мы вынуждены использовать явный интерфейс.В качестве примера случая, в котором мы могли бы выбрать, рассмотрим написание списка только для чтения, который заключает в себе List<T>
и реализует IList<T>
.
Все, что нам нужно здесь сделать, - это делегировать операции чтенияупакованный список и throw не поддерживаются для операций записи, например:
public T this[int idx]
{
get
{
return _inner[idx];
}
set
{
throw new NotSupportedException("Read only list.");
}
}
public int Count
{
get
{
return _inner.Count;
}
}
public void Add(T item)
{
throw new NotSupportedException("Read only list.");
}
public bool IsReadOnly
{
get
{
return false;
}
}
и так далее.Однако это не очень полезно для людей, использующих объект с конкретным типом, у нас есть целая группа членов, которые либо возвращают один и тот же результат (IsReadOnly
), либо всегда выдают исключение.Хотя код, использующий класс через его интерфейс, должен иметь возможность вызывать эти методы и свойства, это не имеет никакого значения для конкретного класса.Следовательно, мы можем сделать следующее:
public T this[int idx]
{
get
{
return _inner[idx];
}
}
T IList<T>.this[int index]
{
get { return this[index]; }
set { throw new NotSupportedException("Collection is read-only."); }
}
public int Count
{
get
{
return _inner.Count;
}
}
void ICollection<T>.Add(T item)
{
throw new NotSupportedException("Read only list.");
}
bool ICollection<T>.IsReadOnly
{
get
{
return false;
}
}
Теперь, пока мы полностью реализуем IList<T>
, мы также предлагаем гораздо более чистый и более полезный интерфейс (в общем, а не в C # специфическом смысле «интерфейса») для пользователей.конкретного класса.
Обратите внимание, что это основано именно на причине, по которой обычно имеет смысл быть неявным: мы сделали все эти члены более неудобными для пользователя конкретного класса, чтобы получить (такойкод должен был бы привести к IList<T>
сначала).Это хорошо, так как единственный результат такого неуклюжего кода бессмыслен или опасен, а делать плохие идеи труднее - это хорошо.Если бы такая неловкость только мешала, это было бы другое дело.