Ответ 1
Очевидно, что невозможно оптимизировать все случаи. Если какой-то объект реализует только IEnumerable<T>
, а не IList<T>
, вам нужно повторить его до конца, чтобы найти последний элемент. Таким образом, оптимизация была бы только для типов, которые реализуют IList<T>
(например, T[]
или List<T>
).
Теперь, действительно ли он оптимизирован в .Net 4.5 DP? Разрешить запуск Отражатель ILSpy:
public static IEnumerable<TSource> Reverse<TSource>(
this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return ReverseIterator<TSource>(source);
}
Хорошо, как выглядит ReverseIterator<TSource>()
?
private static IEnumerable<TSource> ReverseIterator<TSource>(
IEnumerable<TSource> source)
{
Buffer<TSource> buffer = new Buffer<TSource>(source);
for (int i = buffer.count - 1; i >= 0; i--)
{
yield return buffer.items[i];
}
yield break;
}
То, что делает этот блок-итератор, - это создать Buffer<T>
для коллекции и прокрутить назад. Мы почти там, что Buffer<T>
?
[StructLayout(LayoutKind.Sequential)]
internal struct Buffer<TElement>
{
internal TElement[] items;
internal int count;
internal Buffer(IEnumerable<TElement> source)
{
TElement[] array = null;
int length = 0;
ICollection<TElement> is2 = source as ICollection<TElement>;
if (is2 != null)
{
length = is2.Count;
if (length > 0)
{
array = new TElement[length];
is2.CopyTo(array, 0);
}
}
else
{
foreach (TElement local in source)
{
if (array == null)
{
array = new TElement[4];
}
else if (array.Length == length)
{
TElement[] destinationArray = new TElement[length * 2];
Array.Copy(array, 0, destinationArray, 0, length);
array = destinationArray;
}
array[length] = local;
length++;
}
}
this.items = array;
this.count = length;
}
// one more member omitted
}
Что мы здесь? Мы копируем содержимое в массив. В каждом случае. Единственная оптимизация заключается в том, что если мы знаем Count
(т.е. Коллекция реализует ICollection<T>
), нам не нужно перераспределять массив.
Таким образом, оптимизация для IList<T>
не в .Net 4.5 DP. Он создает копию всей коллекции в каждом случае.
Если бы я догадался, почему он не оптимизирован, прочитав статью Jon Skeet по этому вопросу, я думаю, это потому, что эта оптимизация наблюдаемым. Если вы мутируете коллекцию во время итерации, вы увидите измененные данные с оптимизацией, но старые данные без нее. И оптимизация, которая на самом деле изменяет поведение чего-то тонким способом, является плохой, из-за обратной совместимости.