2008-10-21 4 views
32

Это может быть старый вопрос: почему IEnumerable<T> унаследовать от IEnumerable?Почему IEnumerable <T> наследуется от IEnumerable?

Вот как это делает .NET, но это приносит небольшую проблему. Каждый раз, когда я пишу класс, реализует IEumerable<T>, мне нужно написать две функции GetEnumerator(), одну для IEnumerable<T>, а другую для IEnumerable.

И, IList<T> не наследуется от IList.

Я не знаю, почему IEnumerable<T> спроектирован другим способом.

ответ

48

Straight from the horse's mouth (Хейлсберг):

В идеале все общих интерфейсов сбора (например, ICollection<T>, IList<T>) наследует от своих необщего аналоги, такие, что общие экземпляры интерфейса могут быть использованы как с универсальным и не- общий код. Например, было бы удобно, если бы IList<T> мог быть передан в код, который ожидает IList.

Как оказалось, единственный общий интерфейс, для которого это возможно является IEnumerable<T>, потому что только IEnumerable<T> противопоказано вариант: В IEnumerable<T>, параметр типа Т используется только в «выходных» позиции (возвращаемые значения), а не в «входных» позициях (параметрах). ICollection<T> и IList<T> используют T в обоих положениях ввода и вывода, и эти интерфейсы поэтому являются инвариантными. (Как и в сторону, они были бы противопоказаны вариант, если T используется только во входных позициях, но это действительно не имеет значения здесь.)

< ... чик ...>

Итак, чтобы ответить на ваш вопрос, IEnumerable<T> наследует от IEnumerable, потому что он может! :-)

+1

Это очень интересно ... Мне было очень досадно, что `IList ` не наследовал `IList`, но как только вы примете дисперсию во внимание, это начинает иметь смысл ... – 2009-11-29 01:01:04

+1

Плохо, что Microsoft никогда не определяла IReadableByIndex и IReadableByIndex (Of Out T), которые могли быть унаследованы IList и IList (Of T). IReadableByIndex может быть унаследован IReadableByIndex (Of T) без труда, и дисперсия индексируемых коллекций, доступных только для чтения, была бы очень приятной. – supercat 2011-01-16 23:06:01

16

Ответ на вопрос IEnumerable: «потому что он может не влиять на безопасность типа».

IEnumerable - это интерфейс «только для чтения», поэтому не имеет значения, что общая форма более специфична, чем неродная форма. Вы ничего не нарушаете, реализуя оба. IEnumerator.Current возвращает object, тогда как IEnumerator<T>.Current возвращает T - это нормально, так как вы всегда можете законно конвертировать в object, хотя это может означать бокс.

Сравните это с IList<T> и IList - вы можете позвонить Add(object) на IList, а что вполне может быть недействительным для любого конкретного IList<T> (ничего, кроме IList<object> на самом деле).

Brad Abram blogged with Anders' answer примерно этот вопрос.

2

Это так, что он будет работать с классами, которые не поддерживают дженерики. Кроме того, .NET-дженерики не позволяют делать такие вещи, как листинг IList < long> как IList < int>, поэтому не общие версии интерфейсов могут быть весьма полезны, когда вам нужен фиксированный базовый класс или интерфейс.

+0

Это важный момент. IList не может быть отправлен в IList . – 2008-10-21 14:18:39

3

Это для обратной совместимости. Если вы вызываете .Net 1.1, которая ожидает, что ванильный IEnumerable вы можете передать в свой общий IEnumerable.

Luckilly родового IEnumerator наследует от старого стиля IEnumerator

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

private IEnumerator<string> Enumerator() { 
     // ... 
    } 

    public IEnumerator<string> GetEnumerator() { 
     return Enumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { 
     return Enumerator(); 
    }