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

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


person Nawaz    schedule 11.02.2011    source источник


Ответы (4)


В основном неуниверсальные интерфейсы появились первыми в .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();
}

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

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

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

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

person Jon Skeet    schedule 11.02.2011
comment
@Jon: вопрос в том, зачем нам вообще нужно реализовывать IEnumerable, когда мы уже реализуем IEnumerable<T>, или наоборот? - person Nawaz; 12.02.2011
comment
@Nawaz: я отредактировал свой ответ. Насколько вам комфортно с идеей, почему дженерики — это хорошо? - person Jon Skeet; 12.02.2011
comment
@Jon: Редактировать хорошо. благодаря. Теперь я понимаю, что IEnumerable предназначено для обратной совместимости, верно? - person Nawaz; 12.02.2011
comment
@Наваз: В основном. По сути, его использует много кода — вы не можете удалить общедоступный интерфейс после того, как вы его выпустили. - person Jon Skeet; 12.02.2011
comment
Джон, ты действительно потрясающий гуру С#, продолжай в том же духе! Это очень помогло! - person Exitos; 01.12.2011
comment
@RoyiNamir: Непонятно, почему вы спрашиваете об этом в качестве комментария к этому ответу. Если у вас есть отдельный вопрос, задайте его как отдельный вопрос. - person Jon Skeet; 16.07.2012

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

person Edwin Buck    schedule 11.02.2011

Что касается того, почему вы видите классы, определяющие оба, достаточно, хотя 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
person asawyer    schedule 11.02.2011

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

person user2105566    schedule 24.02.2013
comment
Вопрос был не в этом. - person siggi_pop; 25.06.2019