Ответ 1
В основном, нестандартные интерфейсы появились в .NET 1.0 и 1.1. Затем, когда вышла .NET 2.0, появились общие эквиваленты. Жизнь была бы намного проще, если бы дженерики сделали это в .NET 1.0:)
С точки зрения реализации "только" IEnumerable<T>
вместо обоих - вам в основном нужно реализовать оба варианта, и вы также должны использовать явную реализацию интерфейса, учитывая, что оба определяют метод без параметров GetEnumerator
. Поскольку IEnumerator<T>
также расширяет IEnumerator
, он обычно выглядит примерно так:
public IEnumerator<T> GetEnumerator()
{
// Return real iterator
}
// Explicit implementation of nongeneric interface
IEnumerator IEnumerable.GetEnumerator()
{
// Delegate to the generic implementation
return GetEnumerator();
}
С другой стороны, с блоками итератора, введенными в С# 2 (с yield return
и т.д.), вам, к счастью, редко приходится реализовывать эти вещи вручную. Возможно, вам нужно написать что-то вроде выше, а затем использовать yield return
в методе GetEnumerator
.
Обратите внимание, что IList<T>
не распространяется IList
, а ICollection<T>
не распространяется ICollection
. Это потому, что для этого менее безопасным для типа... тогда как любой общий итератор можно рассматривать как неисторичный итератор из-за (потенциально бокс) преобразования любого значения в object
, IList
и ICollection
разрешать значения быть добавлены в коллекцию; и нет смысла добавлять (скажем) строку к IList<int>
.
EDIT: Причина, по которой нам требуется IEnumerable<T>
, состоит в том, чтобы мы могли выполнять итерацию безопасным способом и распространять эту информацию. Если я верну вам IEnumerable<string>
, вы знаете, что можете смело предположить, что все возвращаемое из него будет ссылкой на строку или null. С помощью IEnumerable
нам пришлось эффективно использовать (часто неявно в выражении foreach
) каждый элемент, который был возвращен из последовательности, потому что свойство Current
IEnumerator
имеет тип object
. Что касается того, почему нам еще нужно IEnumerable
- потому что старые интерфейсы никогда не исчезают, в основном. Там слишком много существующего кода.
Было бы возможно, чтобы IEnumerable<T>
не расширялся IEnumerable
, но тогда любой код, который хотел бы использовать IEnumerable<T>
, не мог вызвать метод, принимающий IEnumerable
- и было много такие методы, как .NET 1.1 и 1.0.