JakePerry.ReverseEnumeration 1.1.1

dotnet add package JakePerry.ReverseEnumeration --version 1.1.1
                    
NuGet\Install-Package JakePerry.ReverseEnumeration -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JakePerry.ReverseEnumeration" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JakePerry.ReverseEnumeration" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="JakePerry.ReverseEnumeration" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add JakePerry.ReverseEnumeration --version 1.1.1
                    
#r "nuget: JakePerry.ReverseEnumeration, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package JakePerry.ReverseEnumeration@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=JakePerry.ReverseEnumeration&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=JakePerry.ReverseEnumeration&version=1.1.1
                    
Install as a Cake Tool

JakePerry.ReverseEnumeration

NuGet version (JakePerry.ReverseEnumeration)

A simple package that makes enumerating collections in reverse order easy.

Purpose

Manually enumerating a collection in reverse order can feel tedious and can be prone to index errors (forget the -1 in the initializer section or the >= in the condition and you'll run into an ArgumentOutOfRangeException or miss the first element). A foreach loop is cleaner and convenient.

// Traditional reverse for-loop
for (int i = list.Count - 1; i >= 0; i--)
{
    var obj = list[i];
}

// New foreach loop
foreach (var obj in list.Reversed()) { ... }

Advantages

Immutability

Methods such as Array.Reverse and List<T>.Reverse mutate the original collection which is not always ideal. The ReverseEnumerator<T> does not mutate the original collection.

This package also offers the option to maintain list immutability during reverse-enumeration with the use of the List<T>.ReversedImmutable extension method. When this method is used, an InvalidOperationException will be thrown if the list is modified during enumeration, matching the behavior of the default List<T>.Enumerator type. More info in the Usage/List<T> and mutability section below.

Allocations

The LINQ Enumerable.Reverse extension method:

  • boxes the source enumerable if it's a struct
  • allocates a new enumerable object
  • clones the entire source collection before iteration

The reverse enumerator doesn't allocate any additional memory. The package takes advantage of a compiler optimization by directly returning a value-type enumerator in the GetEnumerator() methods.

Usage

The code lives in the JakePerry namespace, so make sure you include it.

using JakePerry;

Any collection implementing the IList<T> or IReadOnlyList<T> interface can be enumerated in reverse order via the Reversed() extension method. ArraySegment<T> is also supported.

Most commonly, this is used directly in a foreach loop

foreach (var obj in list.Reversed()) { ... }

The returned struct implements IEnumerable, IEnumerable<T>, IReadOnlyCollection<T> and IReadOnlyList<T>, so it can also be passed to any other methods accepting these types as required, at the cost of boxing the object.

List<T> and mutability

As mentioned above, it's possible to guarantee immutability for the List<T> type during reverse-enumeration, just as regular enumeration does. The ReversedImmutable() extension method uses a special enumerator that performs the additional checks required & throws an InvalidOperationException if it's modified during enumeration. Note that this additional overhead is costly (see benchmarks below)

var list = new List<string>() { "a", "b", "c" };

// Using the Reversed method, the list can be modified as required
foreach (var obj in list.Reversed()) { ... }

// Using the ReversedImmutable method, modification during enumeration will cause an InvalidOperationException
foreach (var obj in list.ReversedImmutable()) { ... }

Benchmarks

List enumeration benchmarks

List size: 16

|                         Method |      Mean |    Error |   StdDev | Allocated |
|------------------------------- |----------:|---------:|---------:|----------:|
|                        ForLoop |  12.34 ns | 0.044 ns | 0.041 ns |         - |
|           Standard_ForeachLoop |  61.47 ns | 0.919 ns | 0.860 ns |         - |
|           Reversed_ForeachLoop |  83.78 ns | 0.246 ns | 0.218 ns |         - |
| Immutable_Reversed_ForeachLoop | 269.17 ns | 1.537 ns | 1.200 ns |         - |

List size: 4096

|                         Method |      Mean |     Error |    StdDev | Allocated |
|------------------------------- |----------:|----------:|----------:|----------:|
|                        ForLoop |  2.618 us | 0.0089 us | 0.0079 us |         - |
|           Standard_ForeachLoop | 10.657 us | 0.0562 us | 0.0439 us |         - |
|           Reversed_ForeachLoop | 16.939 us | 0.0753 us | 0.0704 us |         - |
| Immutable_Reversed_ForeachLoop | 57.818 us | 0.3556 us | 0.3152 us |         - |
ArraySegment enumeration benchmarks

ArraySegment count: 16

|               Method |      Mean |     Error |    StdDev |    Median | Allocated |
|--------------------- |----------:|----------:|----------:|----------:|----------:|
|              ForLoop |  7.625 ns | 0.1795 ns | 0.3237 ns |  7.466 ns |         - |
| Reversed_ForeachLoop | 60.111 ns | 1.2133 ns | 2.0272 ns | 59.453 ns |         - |

ArraySegment count: 4096

|               Method |      Mean |     Error |    StdDev | Allocated |
|--------------------- |----------:|----------:|----------:|----------:|
|              ForLoop |  1.332 us | 0.0211 us | 0.0234 us |         - |
| Reversed_ForeachLoop | 11.178 us | 0.2232 us | 0.3909 us |         - |
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 557 1/26/2022
1.0.10 490 1/18/2022
1.0.9 494 1/15/2022
1.0.8 335 1/5/2022