37

For the List object, we have a method called Reverse().
It reverse the order of the list 'in place', it doesn't return anything.

For the IEnumerable object, we have an extension method called Reverse().
It returns another IEnumerable.

I need to iterate in reverse order throught a list, so I can't directly use the second method, because I get a List, and I don't want to reverse it, just iterate backwards.

So I can either do this :

for(int i = list.Count - 1; i >=0; i--)

Or

foreach(var item in list.AsEnumerable().Reverse())

I found it less readable than if I have an IEnumerable, just do

foreach(var item in list.Reverse())

I can't understand why this 2 methods have been implemented this way, with the same name. It is pretty annoying and confusing.

Why there is not an extension called BackwardsIterator() in the place of Reverse() working for all IEnumerable?

I'm very interested by the historical reason of this choice, more than the 'how to do it' stuff!

Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
  • 2
    You could use Enumerable.Reverse(list) – Alexander Efimov Sep 12 '12 at 14:55
  • 1
    Why don't you just have your list typed to the type that you like, and then it will use the version of the method you like? – Brian Warshaw Sep 12 '12 at 14:56
  • ^ presumably he "likes" it to be a `List`, since it is a `List` -- but, as he clearly said, he doesn't want to use `List.Reverse`. "typed to the type that you like" means casting it, which isn't any prettier than using `AsEnumerable()`. – Jim Balter Nov 24 '18 at 06:37

3 Answers3

29

It is worth noting that the list method is a lot older than the extension method. The naming was likely kept the same as Reverse seems more succinct than BackwardsIterator.

If you want to bypass the list version and go to the extension method, you need to treat the list like an IEnumerable<T>:

var numbers = new List<int>();
numbers.Reverse(); // hits list
(numbers as IEnumerable<int>).Reverse(); // hits extension

Or call the extension method as a static method:

Enumerable.Reverse(numbers);

Note that the Enumerable version will need to iterate the underlying enumerable entirely in order to start iterating it in reverse. If you plan on doing this multiple times over the same enumerable, consider permanently reversing the order and iterating it normally.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • 2
    Actually, even if one is only going to want to enumerate the thing once and wanted to keep the original list undisturbed, I would expect that it would almost certainly be faster to call `ToArray()` and then call `Reverse()` on the resulting array, than to use `Enumerable.Reverse()`. The `List.ToArray()` method can store data in the target array as a block without having to process individual items, while `Enumerable.Reverse()` will--unless it has special handling for the case where the source is a `List`--have to copy items into an array one at a time before reversing them. – supercat Sep 13 '12 at 16:53
  • 1
    @Supercat The chances are good that it has special casing. I've used ILSpy on some other methods in the past and they were special cases on `IList` a lot of the time. – Adam Houldsworth Sep 13 '12 at 17:41
  • 1
    There are indeed many special cases, but there are also some annoying wrinkles. For example, although a `List` may be passed to code which expects an `IEnumerable`, such code will not be able to exploit any special "list-ness". My "almost certainly" language was perhaps a bit strong, though an advantage of converting to an array before enumeration is that if I recall correctly C# generates special code when using a `foreach` loop on something it knows to be an array. Even if `Enumerable.Reverse` returns the enumerator of the temporary array, the compiler won't know that. – supercat Sep 13 '12 at 17:53
  • @supercat It's definitely a good shout, plus you get the added benefit of not relying on the inner workings of the current linq implementation for any performance gains. – Adam Houldsworth Sep 13 '12 at 18:00
  • If you want to play code golf, instead of the cast I would rather call `numbers.Skip(0).Reverse()`, it is more readable from left to right, and you are dealing with an `IEnumerable`. It is also nicer than `numbers.OfType().Reverse()` since it doesn't put in the overhead of a useless type check on every entry. – Nameless One Apr 14 '15 at 17:12
  • 1
    There is also `numbers.Reverse()`, which one of the extension's overloads. – heltonbiker Sep 30 '15 at 20:19
  • With the availability of the .Net Framework Reference Source, you can see that `Enumerable.Reverse` has special handling for `ICollection<>` which adds a `Count` and `CopyTo` operation; special handling for `IList<>` would seem to be worthwhile but isn't present. – NetMage Jul 02 '18 at 17:50
  • *It is also nicer than numbers.OfType().Reverse() since it doesn't put in the overhead of a useless type check on every entry* -- quite the strawman. The OP suggested the sensible `AsEnumerable()`, not `OfType()`. *There is also numbers.Reverse(), which one of the extension's overloads* -- that's not an "overload", and it will still invoke `List.Reverse`, not `IEnumerable.Reverse`. – Jim Balter Nov 24 '18 at 09:00
5

Write your own BackwardsIterator then!

public static IEnumerable BackwardsIterator(this List lst)
{
    for(int i = lst.Count - 1; i >=0; i--)
    {
        yield return lst[i];
    }
}
Anirudha
  • 32,393
  • 7
  • 68
  • 89
  • 2
    If you're going to make a utility method out of it you should use a more efficient method (i.e. a backwards for loop). `Reverse`, as an IEnumerable, is rather inefficient as it needs to iterate the enumerable, store it in a buffer, and then iterate the buffer in reverse. – Servy Sep 12 '12 at 15:05
  • @Servy you are right..specially if the list is `huge`!..thx to point it out – Anirudha Sep 12 '12 at 15:10
2

The existence of List<T>.Reverse long preceded the existence of IEnumerable<T>.Reverse. The reason they are named the same is ... incompetence. It's a horrible botch; clearly the Linq IEnumerable<T> function should have been given a different name ... e.g., Backwards ... since they have quite different semantics. As it is, it lays an awful trap for programmers -- someone might change the type of list from List<T> to, e.g., Collection<T>, and suddenly list.Reverse();, rather than reversing list in place, simply returns an IEnumerable<T> that is discarded. It cannot be overstated just how incompetent it was of MS to give these methods the same name.

To avoid the problem you can define your own extension method

public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) => source.Reverse();

You can even add a special case for efficient processing of indexable lists:

public static IEnumerable<T> Backwards<T>(this IEnumerable<T> source) =>
    source is IList<T> list ? Backwards<T>(list) : source.Reverse();

public static IEnumerable<T> Backwards<T>(this IList<T> list)
{
    for (int x = list.Count; --x >= 0;)
        yield return list[x];
}
Jim Balter
  • 16,163
  • 3
  • 43
  • 66