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

Разница между IEnumerable и IEnumerable <T>?

В чем разница между IEnumerable и IEnumerable<T>?

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

Посмотрите, как они были определены:

public interface IEnumerable
{
    [DispId(-4)]
    IEnumerator GetEnumerator();
}
public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Как мы видим, IEnumerable<T> происходит от IEnumerable, это означает, что любой IEnumerable имеет, IEnumerable<T> наследует, то почему мы реализуем оба, а не только IEnumerable<T>? Не реализует IEnumerable<T> недостаточно?

Аналогично, существуют и другие аналогичные пары:

  • IList и IList<T>
  • ICollection и ICollection<T>

Я также хотел бы узнать об этом.

4b9b3361

Ответ 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.

Ответ 2

Один возвращает объект типа Object, другой возвращает объект типа T.

Ответ 3

Что касается того, почему вы видите классы, определяющие оба, достаточно, хотя IEnumerable <T> реализует IEnumerable, его не нужно, но он хорош в самостоятельной документации, чтобы перечислить вспомогательные интерфейсы. Рассмотрим

interface IA { }
interface IB : IA { }

class A : IB {} // legal, IB implements IA

class B : IA, IB {} // also legal, possible more clear on intent

Ответ 4

При повторении через цикл IEnumerator поддерживает состояние. Он запоминает позицию курсора, а IEnumerable этого не делает.