Можно повторить назад через foreach?

Я знаю, что мог бы использовать for заявление и достичь того же эффекта, но могу ли я петлю назад через foreach цикл в C#?

11 ответов


при работе со списком (прямая индексация) вы не можете сделать это так же эффективно, как с помощью for петли.

Edit: что обычно означает, когда вы в состоянии использовать for loop, это, вероятно, правильный метод для этой задачи. Плюс, как foreach реализован в порядке, сама конструкция построена для выражения циклов, не зависящих от индексов элементов и порядка итераций, что особенно важно в параллельно Программирование. Это мое мнение эта итерация, полагающаяся на порядок, не должна использовать foreach для лупинга.


если вы находитесь на .NET 3.5, вы можете сделать это:

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

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

если у вас есть коллекция с прямым индексированием (например, IList), вы обязательно должны использовать for вместо петли.

если вы находитесь на .NET 2.0 и не можете использовать цикл for (т. е. у вас просто есть IEnumerable), вам просто нужно будет написать ваша собственная обратная функция. Это должно сработать:

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}

Это зависит от некоторого поведения, которое, возможно, не так очевидно. Когда вы передаете IEnumerable конструктору стека, он будет перебирать его и помещать элементы в стек. Когда вы затем повторяете стек, он выводит вещи обратно в обратном порядке.

это и .NET 3.5 Reverse() метод расширения, очевидно, взорвется, если вы подадите ему IEnumerable, который никогда не перестанет возвращаться предметы.


как говорит 280Z28, для IList<T> вы можете просто использовать индекс. Вы можете скрыть это в методе расширения:

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

это будет быстрее, чем Enumerable.Reverse() который буферизует все данные в первую очередь. (Я не верю Reverse имеет какие-либо оптимизации, применяемые таким образом, что Count() делает.) Обратите внимание, что эта буферизация означает, что данные считываются полностью при первом запуске итерации, тогда как FastReverse будет "видеть" любые изменения, внесенные в список во время итерации. (Он также сломается, если вы удалите несколько элементов между итерациями.)

для общих последовательностей нет возможности итерации в обратном порядке-последовательность может быть бесконечной, например:

public static IEnumerable<T> GetStringsOfIncreasingSize()
{
    string ret = "";
    while (true)
    {
        yield return ret;
        ret = ret + "x";
    }
}

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


прежде чем использовать "foreach" для итерации, переверните список методом "reverse":

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }

Если вы используете List, вы также можете использовать этот код:

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();

Это метод, который пишет обратный список сам по себе.

Теперь foreach:

foreach(string s in list)
{
    Console.WriteLine(s);
}

выход:

3
2
1

нет. ForEach просто перебирает коллекцию для каждого элемента, и порядок зависит от того, использует ли он IEnumerable или GetEnumerator().


Это возможно, если вы можете изменить код который реализует IEnumerable или IEnumerable (например, ваша собственная реализация IList).

создать итератор выполнение этой работы для вас, например, как следующая реализация через IEnumerable интерфейс (предполагая, что "элементы" - это поле списка в этом примере):

public IEnumerator<TObject> GetEnumerator()
{
    for (var i = items.Count - 1; i >= 0; i--)
    { 
        yield return items[i];
    }
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

из-за этого ваш список будет повторен в обратном порядке через список.

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


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

метод расширения Linq с использованием анонимных типов с Linq Select для предоставления ключа сортировки для Linq OrderByDescending;

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

использование:

    var eable = new[]{ "a", "b", "c" };

    foreach(var o in eable.Invert())
    {
        Console.WriteLine(o);
    }

    // "c", "b", "a"

Он называется "инвертировать", потому что он является синонимом "обратного" и позволяет неоднозначность с реализацией обратного списка.

можно также отменить определенные диапазоны коллекции, начиная с Int32.Minvalue и типа int32.MaxValue находятся вне диапазона любого типа индекса коллекции, мы можем использовать их для процесса заказа; если индекс элемента ниже заданного диапазона, ему присваивается Int32.MaxValue, чтобы его порядок не менялся при использовании OrderByDescending, аналогично, будут назначены элементы с индексом больше заданного диапазона Типа int32.MinValue, так что они появляются до конца процесса заказа. Всем элементам в пределах заданного диапазона присваивается их нормальный индекс и соответственно реверсируется.

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

использование:

    var eable = new[]{ "a", "b", "c", "d" };

    foreach(var o in eable.Invert(1, 2))
    {
        Console.WriteLine(o);
    }

    // "a", "c", "b", "d"

Я не уверен в производительности этих реализаций Linq по сравнению с использованием временного списка для обертывания коллекции для реверсирования.


на момент написания статьи я не знал о собственной обратной реализации Linq, тем не менее, было весело работать над этим из. https://msdn.microsoft.com/en-us/library/vstudio/bb358497 (v=против 100).aspx


разработка slighty на хороший ответ по Джон Скит, это может быть универсальным:

public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
    if (Forwards) foreach (T item in items) yield return item;
    else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}

а затем использовать как

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}

я использовал этот код, который работал

                if (element.HasAttributes) {

                    foreach(var attr in element.Attributes().Reverse())
                    {

                        if (depth > 1)
                        {
                            elements_upper_hierarchy_text = "";
                            foreach (var ancest  in element.Ancestors().Reverse())
                            {
                                elements_upper_hierarchy_text += ancest.Name + "_";
                            }// foreach(var ancest  in element.Ancestors())

                        }//if (depth > 1)
                        xml_taglist_report += " " + depth  + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + "   =   " + attr.Value + "\r\n";
                    }// foreach(var attr in element.Attributes().Reverse())

                }// if (element.HasAttributes) {

это работает довольно хорошо

List<string> list = new List<string>();

list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");

foreach (String s in list)
{
    Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}