tags:

views:

1217

answers:

5

I know I could use a for statement and achieve the same effect, but what I want to know is, can I loop backwards through a foreach loop in C#?

+20  A: 

If you are on .Net 3.5 you can do this:

var enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

It isn't very efficient as it has to basically go through the enumerator forwards putting everything on a stack then pops everything back out in reverse order.

If you have a directly-indexable collection (e.g. IList) you should definitely use a for loop instead.

If you are on .Net 2.0 and cannot use a for loop (i.e. you just have an IEnumerable) then you will just have to write your own Reverse function, this should work:

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

This relies on some behaviour which is perhaps not that obvious. When you pass in an IEnumerable to the stack constructor it will iterate through it and push the items onto the stack. When you then iterate through the stack it pops things back out in reverse order.

This and the .Net 3.5 Reverse() extension method will obviously blow up if you feed it an IEnumerable which never stops returning items.

Matt Howells
Also I forgot to mention .net v2 only please
JL
Good post, useful information here.
Gordon Mackie JoanMiro
Interesting .NET 2.0 solution.
RichardOD
+9  A: 

When working with a list (direct indexing), you cannot do it as efficiently as using a for loop.

Edit: Which generally means, when you are able to use a for loop, it's likely the correct method for this task. Plus, for as much as foreach is implemented in-order, the construct itself is built for expressing loops that are independent of element indexes and iteration order, which is particularly important in parallel programming. It is my opinion that iteration relying on order should not use foreach for looping.

280Z28
+2  A: 

No. ForEach just iterates through collection for each item and order depends whether it uses IEnumerable or GetEnumerator().

Josip Medved
Well there *are* guarantees of order, depending on the collection type.
Jon Skeet
Correct. My mistake.
Josip Medved
+6  A: 

As 280Z28 says, for an IList<T> you can just use the index. You could hide this in an extension method:

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

This will be faster than Enumerable.Reverse() which buffers all the data first. (I don't believe Reverse has any optimisations applied in the way that Count() does.) Note that this buffering means that the data is read completely when you first start iterating, whereas FastReverse will "see" any changes made to the list while you iterate. (It will also break if you remove multiple items between iterations.)

For general sequences, there's no way of iterating in reverse - the sequence could be infinite, for example:

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

What would you expect to happen if you tried to iterate over that in reverse?

Jon Skeet
Just curiosity, why use ">= 0" instead of "> -1"?
Chris S
> why use ">= 0" instead of "> -1"? Because >= 0 better communicates the intent to humans reading the code. The compiler ought to be be able to optimize that to the equivalent > -1 if doing so would improve performance.
Mark Maslar
FastReverse(this IList<T> items) should be FastReverse<T>(this IList<T> items. :)
unforgiven3
A: 

This works pretty well

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]);
}
Mc_Topaz
This sounds incredibly inefficient to me.
Jean Azzopardi