Почему в С# нет ReverseEnumerator?

Кто-нибудь знает, есть ли конкретная причина или дизайнерское решение, чтобы не включать обратный счетчик в С#? Было бы так приятно, если бы был эквивалент С++ reverse_iterator, так как Enumerator является эквивалентом С++ iterator. Коллекции, которые могут быть обратными итерациями, будут просто реализовывать что-то вроде IReverseEnumerable, и можно было бы сделать что-то вроде:

List<int>.ReverseEnumerator ritr = collection.GetReverseEnumerator();
while(rtir.MoveNext())
{
 // do stuff
}

Таким образом, вы могли бы выполнять итерацию списков и LinkedLists таким же образом, а не использовать индексатор для одной и предыдущих ссылок для другого, тем самым обеспечивая лучшую абстракцию

Ответ 1

Было бы вполне возможно реализовать это. Лично я почти никогда не обращаю назад. Если мне нужно это сделать, я сначала вызываю .Reverse(). Вероятно, это то, о чем подумали и дизайнеры .NET BCL.

По умолчанию все функции не реализованы. Они должны разрабатываться, внедряться, тестироваться, документироваться и поддерживаться. - Раймонд Чен

И вот почему вы не реализуете функции, которые мало полезны. Вы начинаете с самых важных функций (например, итерации спереди-назад). И вы останавливаетесь где-нибудь, где либо ваш бюджет исчерпан, либо где вы думаете, что нет смысла продолжать.

В библиотеке базового класса .NET есть много вещей. До .NET 4 даже не было File.EnumerateLines. И я бы рискнул сказать, что такая функциональность важнее, чем обратная итерация для большинства людей.

Возможно, вы работаете в бизнес-домене, где обратная итерация распространена. Мой опыт противоположный. В качестве дизайнера фреймворков вы можете только догадываться, кто будет использовать вашу инфраструктуру и какие функции потребуют эти люди. Трудно провести линию.

Ответ 2

Он недоступен, потому что IEnumerable является только итератором только вперед. Он имеет только метод MoveNext(). Это делает интерфейс очень универсальным и ядром Linq. Существует множество коллекций реального мира, которые нельзя повторять заново, потому что это требует хранения. Большинство потоков, как это, например.

Linq предоставляет решение с помощью метода расширения Reverse(). Он работает, сначала сохраняя элементы, а затем итерации их назад. Это, однако, может быть очень расточительным, оно требует хранения O (n). Отсутствует возможная оптимизация для уже индексируемых коллекций. Что вы можете исправить:

static class Extensions {
    public static IEnumerable<T> ReverseEx<T>(this IEnumerable<T> coll) {
        var quick = coll as IList<T>;
        if (quick == null) {
            foreach (T item in coll.Reverse()) yield return item;
        }
        else {
            for (int ix = quick.Count - 1; ix >= 0; --ix) {
                yield return quick[ix];
            }
        }
    }
}

Использование образца:

        var list = new List<int> { 0, 1, 2, 3 };
        foreach (var item in list.ReverseEx()) {
            Console.WriteLine(item);
        }

Вы хотите сделать специализацию для LinkedList, поскольку она не реализует IList < > , но все же позволяет ускорить обратную итерацию с помощью свойств Last и LinkedListNode.Previous. Хотя гораздо лучше не использовать этот класс, он имеет паршивость локального процессора. Всегда предпочитайте List < > , когда вам не нужны дешевые вставки. Это может выглядеть так:

    public static IEnumerable<T> ReverseEx<T>(this LinkedList<T> list) {
        var node = list.Last;
        while (node != null) {
            yield return node.Value;
            node = node.Previous;
        }
    }

Ответ 3

Подсказка в заключительной строке OP: использование этого в списках и списках ссылок.

Итак, для List этот будет хорошо работать:

    public static IEnumerable<T> AsReverseEnumerator<T>(this IReadOnlyList<T> list)
    {
        for (int i = list.Count; --i >= 0;) yield return list[i];
    }

Использование IReadOnlyList дает большую гибкость с точки зрения того, над чем он будет работать.

Нечто подобное было бы возможно для LinkedLists.

Ответ 4

Из документов: рекомендуется использовать foreach вместо прямого манипулирования перечислением (см. http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx). Итак, Тревор прав, вы можете просто сделать:

foreach(var el in collection.Reverse())
{
}

Функция Reverse() не олицетворяет коллекцию, поэтому небольшие накладные расходы по сравнению с итерацией с обратной стороны.