Есть ли причина скрывать унаследованные элементы в интерфейсе?

Я понимаю, что класс, который наследуется от другого класса, может скрыть свойство с помощью ключевого слова new. Это, однако, скрывает конкретную реализацию свойства, поэтому я могу видеть, как его можно использовать.

Есть ли какая-либо практическая причина, чтобы скрыть элементы интерфейса, которые реализуют другие интерфейсы? Например, рассмотрим пример ниже. IChildInterface реализует IParentInterface и скрывает PropertyA.

interface IParentInterface
{
    string Name { get; set; }
    int PropertyA { get; set; }
    int PropertyB { get; set; }
}

interface IChildInterface : IParentInterface
{
    int PropertyA { get; set; }
    int PropertyC { get; set; }
}

Ответ 1

Есть ли какая-либо практическая причина для скрытия элементов в интерфейсах, которые реализуют другие интерфейсы?

Конечно. Тот факт, что сам BCL использует этот шаблон, свидетельствует о том, что шаблон практичен. Например:

interface IEnumerable 
{ 
    IEnumerator GetEnumerator();
}
interface IEnumerable<T> : IEnumerable
{
    new IEnumerator<T> GetEnumerator();
}

Дизайнеры IEnumerable<T> хотели быть обратно совместимыми с IEnumerable, но также хотели убедиться, что каждое использование GetEnumerator в общем интерфейсе называется универсальной версией. Скрытие - это соответствующий механизм в этом случае.

Для некоторого дополнительного обсуждения тонких точек о скрытии метода см.:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx

Ответ 2

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

public interface IBase
{
   int MyProperty { get; }
}

public interface IDerive : IBase
{
    // you need to specify the getter here too
    new int MyProperty { get; set; }
}

Ответ 3

Интерфейсы не могут полностью скрыть родительские интерфейсы, но класс реализации может быть полезен.

Рассмотрим класс MyStringList, который является списком только для чтения, который реализует IList<string>. Мы будем использовать это как простой проход для простоты: некоторые из членов довольно бессмысленны, поэтому мы можем сделать следующее:

//implement this one implicitly, as it useful.
public int Count
{
  return _list.Count;
}
//do a half-and-half on the indexer
public string this[int index]
{
  get
  {
    return _list[index];
  }
}
string IList<string>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
//hide some pointless ones.
bool ICollection<string>.IsReadOnly
{
  get
  {
    return true;
  }
}
void IList<string>.Insert(int index, string item)
{
  throw new NotSupportedException("Collection is read-only.");
}
void IList<string>.RemoveAt(int index)
{
  throw new NotSupportedException("Collection is read-only.");
}
void ICollection<string>.Add(string item)
{
  throw new NotSupportedException("Collection is read-only.");
}
void ICollection<string>.Clear()
{
  throw new NotSupportedException("Collection is read-only.");
}
bool ICollection<string>.Remove(string item)
{
  throw new NotSupportedException("Collection is read-only.");
}

Кто-то, кто имеет дело с интерфейсом MyStringList через IList<string>, должен иметь возможность называть эти бессмысленные члены, но для этого не нужно иметь дело с MyStringList.

Теперь, когда это возможно для класса, интерфейс может заставить такую ​​импликацию на класс по имени, сопоставляющему родительский интерфейс. Пример класса IEnumberable<T>, где GetEnumerator() соответствует значению IEnumerable, из которого он наследуется. Следовательно, класс может неявно реализовать только один и должен скрывать другое или оба (так как всегда можно отнести результат (a IEnumerator<T>) к результирующему типу другого (a IEnumerator), тогда наиболее разумное поведение как правило, неявно реализовать версию IEnumberable<T> и явно реализовать IEnumerable один (обычно, возвращая результат другого).

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

Ответ 4

Скрытие унаследованных членов никогда не следует делать намеренно как часть дизайна. Скрытие языка разрешено языком для предотвращения изменений в предку (в основных выпусках библиотеки) от взлома потомков, у которых уже есть член с тем же именем.

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

См. также: Есть ли ситуация, когда производный класс должен скрывать...?

Ответ 5

Я полагаю, что если вы собираетесь реализовать IChildInterface явно, может быть случай, когда вы хотите скрыть PropertyA (возможно?)