Разница между IEnumerable и IEnumerable <T>? - PullRequest
45 голосов
/ 11 февраля 2011

В чем разница между 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>

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

Ответы [ 4 ]

56 голосов
/ 11 февраля 2011

В основном неуниверсальные интерфейсы были первыми, в .NET 1.0 и 1.1. Затем, когда вышел .NET 2.0, вышли общие эквиваленты. Жизнь была бы намного проще, если бы дженерики превратили ее в .NET 1.0:)

С точки зрения реализации "only" 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();
}

С другой стороны, с блоками итераторов, введенными в C # 2 (с yield return и т. Д.), К счастью, вам редко приходится реализовывать эти вещи полностью вручную. Возможно, вам придется написать что-то похожее на приведенное выше, а затем использовать yield return в методе GetEnumerator.

Обратите внимание, что IList<T> не не расширяет IList, а ICollection<T> делает не расширяет ICollection. Это потому, что это менее безопасно для типов ... в то время как любой универсальный итератор может рассматриваться как неуниверсальный итератор из-за (потенциально упаковочного) преобразования любого значения в object, IList и ICollection позволяют значениям быть добавленным в коллекцию; и нет смысла добавлять (скажем) строку в IList<int>.

РЕДАКТИРОВАТЬ: Причина, по которой нам нужно IEnumerable<T>, заключается в том, что мы можем выполнять итерации безопасным для типов способом и распространять эту информацию по всему. Если я верну вам IEnumerable<string>, вы знаете, что можете с уверенностью предположить, что все, что будет возвращено из него, будет строковой ссылкой или нулем. С IEnumerable нам пришлось эффективно приводить (часто неявно в операторе foreach) каждый элемент, который был возвращен из последовательности, потому что свойство Current IEnumerator имеет тип object. Что касается того, почему нам все еще нужно IEnumerable - потому что старые интерфейсы никогда не исчезают, в основном. Там слишком много существующего кода, использующего его.

Возможно, что IEnumerable<T> не сможет расширить IEnumerable, но тогда любой код, желающий использовать IEnumerable<T>, не сможет вызвать метод, принимающий IEnumerable - и было много такие методы из .NET 1.1 и 1.0.

7 голосов
/ 11 февраля 2011

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

2 голосов
/ 11 февраля 2011

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

interface IA { }
interface IB : IA { }

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

class B : IA, IB {} // also legal, possible more clear on intent
1 голос
/ 25 февраля 2013

Во время итерации цикла IEnumerator поддерживает состояние. Он запоминает положение курсора, а IEnumerable - нет.

...