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