Who knows Enumerable.Range(...)?






4.97/5 (13 votes)
Make loops behave deterministic-by-construction
Introduction
I quite often see constructs like:
for(int n = 0; n < len; ++n)
{
//... some more or less complex conditional control flow...
n = 5; // causes the loop to never terminate if len is greater than 5
//... some more code ...
}
This is a legal language construct, but it introduces the danger of non-determinism.
This tip aims to show some deterministic alternatives to that plain for
loop.
Using the Code
If you need to loop over some range of integer, consider using the Enumerable.Range[^] based foreach
loop instead:
using System.Linq;
...
foreach (int n in Enumerable.Range(0, len))
{
...
}
This guarantees that it iterates over all elements in strict sequence. Another benefit is that you cannot assign any other value to the loop variable n
. E.g.
foreach (int n in Enumerable.Range(0, len))
{
n = 5; // Compiler error!
}
Note: Enumerable.Range(...)
is not from, to, but from, count:
//
// Summary:
// Generates a sequence of integral numbers within a specified range.
//
// Parameters:
// start:
// The value of the first integer in the sequence.
//
// count:
// The number of sequential integers to generate.
//
// Returns:
// An IEnumerable<Int32> in C# or IEnumerable(Of Int32) in Visual Basic that
// contains a range of sequential integral numbers.
//
// Exceptions:
// System.ArgumentOutOfRangeException:
// count is less than 0.-or-start + count -1 is larger than System.Int32.MaxValue.
public static IEnumerable<int> Range(int start, int count);
Alternatives
One could also build his own iterator functions (see also C# Iterator Pattern demystified[^]). E.g.
public static IEnumerable<int> CountUp(int n, int count)
{
while (count-- > 0) yield return n++;
}
public static IEnumerable<int> CountDown(int n, int count)
{
while (count-- > 0) yield return n--;
}
When used like this...
foreach (int n in CountUp(0, 5)) Console.WriteLine(n);
foreach (int n in CountDown(100, 5)) Console.WriteLine(n);
...results in:
0
1
2
3
4
100
99
98
97
96
You can define any complexity of traversing sequence in that function and let the foreach
-loop terminate deterministically, based on that sequence.
Advise
Try to avoid plain for (...)
loops and replace by some deterministic loop alternative like:
foreach( ... Range(...))
foreach( ... CountUp(...))
- etc.
Other alternatives are Linq iterations like Enumerable.Aggregate[^] etc. But these are a bit more advanced.
History
- 2012-04-18 First version
- 2012-04-19 Added hand-crafted
CountUp
/CountDown
functions - 2013-04-14 Fixed some broken HTML markup in C# code generics