Подтвердить что ты не робот

Почему IEnumerable <T> наследует от IEnumerable?

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

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

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

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

4b9b3361

Ответ 1

Прямо от устья лошади (Хейлсберг):

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

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

<... надрез... >

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

Ответ 2

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

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

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

Брэд Абрам написал в блоге с ответом Андерса об этом самом вопросе.

Ответ 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();
    }

Ответ 4

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