Почему существует две совершенно разные версии Reverse для List и IEnumerable?
Для объекта List
у нас есть метод Reverse().
Он отменяет порядок списка "на месте", он ничего не возвращает.
Для объекта IEnumerable
у нас есть метод расширения, называемый Reverse().
Он возвращает другой IEnumerable.
Мне нужно выполнить итерацию в обратном порядке через список, поэтому я не могу напрямую использовать второй метод, потому что я получаю список, и я не хочу его менять, просто повторяю назад.
Поэтому я могу либо сделать это:
for(int i = list.Count - 1; i >=0; i--)
или
foreach(var item in list.AsEnumerable().Reverse())
Я нашел его менее читаемым, чем если бы у меня был IEnumerable, просто сделайте
foreach(var item in list.Reverse())
Я не могу понять, почему эти 2 метода были реализованы таким образом, с тем же именем. Это довольно раздражает и запутывает.
Почему нет расширения под названием BackwardsIterator() вместо Reverse() для всех IEnumerable?
Меня очень интересует историческая причина этого выбора, больше, чем "как это сделать"!
Ответы
Ответ 1
Стоит отметить, что метод списка намного старше метода расширения. Именование, вероятно, сохранилось так же, как Reverse
кажется более кратким, чем BackwardsIterator
.
Если вы хотите обойти версию списка и перейти к методу расширения, вам нужно обработать список как IEnumerable<T>
:
var numbers = new List<int>();
numbers.Reverse(); // hits list
(numbers as IEnumerable<int>).Reverse(); // hits extension
Или вызовите метод расширения как статический метод:
Enumerable.Reverse(numbers);
Обратите внимание, что для версии Enumerable
необходимо выполнить итерацию перечислимого элемента целиком, чтобы начать повторять его в обратном порядке. Если вы планируете делать это несколько раз по тому же перечислимому, считайте, что вы постоянно меняете порядок и итерации его обычно.
Ответ 2
Создайте свой собственный BackwardsIterator!
public static IEnumerable BackwardsIterator(this List lst)
{
for(int i = lst.Count - 1; i >=0; i--)
{
yield return lst[i];
}
}
Ответ 3
Разница связана с тем, что List
является классом, а IEnumerable
- интерфейсом. Это означает, что объект List имеет одну реализацию (ну, это реализация), и эффект Reverse
гарантированно сохраняется во внутреннем состоянии объекта.
С IEnumerable
он отличается: любой объект может его реализовать, без обязательства реализовать метод Reverse
, потому что он не является частью интерфейса. Метод расширения Reverse()
может только перечислять перечислитель IEnumerable
в обратном порядке, но никогда не изменять внутреннее состояние объекта.
Но List.Reverse
уже существовало, когда Enumerable
и его методы расширения были введены. Если бы они были изобретены в то же время, List.Reverse
также мог бы быть апатридом для равномерного распределения. Кто знает.