Как работает IEnumerable <T>.Reverse? - PullRequest
21 голосов
/ 27 июня 2009

Я проверяю код в отражателе, но я еще не выяснил, как он может перечислять в обратном порядке коллекцию?

Поскольку информации о счетчике нет, и перечисление всегда начинается с «начала» коллекции, верно?

Это недостаток в .NET Framework? Выше ли стоимость обычного перечисления?

Ответы [ 4 ]

41 голосов
/ 27 июня 2009

Короче говоря, он буферизует все, а затем идет назад. Неэффективно, но, с другой стороны, OrderBy тоже не подходит.

В LINQ-to-Objects есть операции буферизации (Reverse, OrderBy, GroupBy и т. Д.) И операции без буферизации (Where, Take, Skip и т. Д.).


В качестве примера реализации без буферизации Reverse, использующей IList<T>, рассмотрим:

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

Обратите внимание, что это все еще немного восприимчиво к ошибкам, если вы изменяете список во время итерации ... так что не делайте этого; -p

6 голосов
/ 27 июня 2009

Работает, копируя базовый IEnumerable в массив, а затем перечисляя его в обратном порядке. Если базовый IEnumerable реализует ICollection (например, T [], List и т. Д.), То шаг копирования пропускается, и перечислитель просто перебирает базовую коллекцию напрямую.

Для получения дополнительной информации посмотрите System.Linq.Buffer в Reflector.

Редактировать: базовая коллекция всегда копируется, даже если это ICollection . Это предотвращает распространение изменений в базовой коллекции буфером .

3 голосов
/ 27 июня 2009

загружает все элементы в память и затем проходит через них (в обратном направлении). это гораздо менее эффективно.

0 голосов
/ 29 октября 2011

Редактировать: Оппс, написал неправильный тест для обратного, мои извинения за неправильный ответ. Это делает буфер после исправления теста (используя перечислимое, возвращаемое Reverse ())

Похоже, метод обратного расширения работает только при заполнении коллекции. При использовании yield return он ничего не делает.

Столкнулся с проблемой, используя обратную мысль, что он должен буферизоваться, чтобы он работал, обнаружил, что он не работает с yield. Это просто пройти и ничего не делать. ниже мой тестовый код.

        [TestMethod]
    public void loopTest()
    {
        var series = this.GetSeries();

        series.Reverse();

        foreach (var l in series)
        {
            Debug.WriteLine(l);
        }
    }

    private IEnumerable<long> GetSeries()
    {
        var series = new List<long>() { 1, 2, 3, 4 };

        foreach (var entry in series)
        {
            Debug.WriteLine(entry);

            yield return entry;
        }
    }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...