Thoughts on foreach with Enumerable.Range versus traditional for loop

In C # 3.0, I like this style:

// Write the numbers 1 thru 7 foreach (int index in Enumerable.Range( 1, 7 )) { Console.WriteLine(index); } 

according to the traditional for loop:

 // Write the numbers 1 thru 7 for (int index = 1; index <= 7; index++) { Console.WriteLine( index ); } 

Assuming n is small, so performance is not a problem, does anyone mind the new style compared to the traditional one?

+61
c # for-loop
May 27 '09 at 1:36 p.m.
source share
17 answers

I believe the latter minimum-maximum format is much clearer than Range -minimum-score for this purpose. In addition, I don’t think it’s really good practice to make a change from the norm that does not happen faster, not shorter, no more familiar and, obviously, more obscure.

Nevertheless, I am not against the idea as a whole. If you came up to me with a syntax that looked something like foreach (int x from 1 to 8) , I would probably agree that this would be an improvement over the for loop. However, Enumerable.Range pretty awkward.

+50
May 27 '09 at 14:00
source share

It is just for fun. (I would just use the standard loop format " for (int i = 1; i <= 10; i++) ".)

 foreach (int i in 1.To(10)) { Console.WriteLine(i); // 1,2,3,4,5,6,7,8,9,10 } // ... public static IEnumerable<int> To(this int from, int to) { if (from < to) { while (from <= to) { yield return from++; } } else { while (from >= to) { yield return from--; } } } 

You can also add the Step extension method:

 foreach (int i in 5.To(-9).Step(2)) { Console.WriteLine(i); // 5,3,1,-1,-3,-5,-7,-9 } // ... public static IEnumerable<T> Step<T>(this IEnumerable<T> source, int step) { if (step == 0) { throw new ArgumentOutOfRangeException("step", "Param cannot be zero."); } return source.Where((x, i) => (i % step) == 0); } 
+38
May 27 '09 at 15:24
source share

In C # 6.0 using

 using static System.Linq.Enumerable; 

you can simplify it to

 foreach (var index in Range(1, 7)) { Console.WriteLine(index); } 
+12
Apr 13 '16 at 7:27
source share

This seems to be a rather long approach to a problem that has already been resolved. Behind Enumerable.Range a whole state machine that is not really needed.

The traditional format is fundamental to development and is familiar to everyone. I do not see any advantage for your new style.

+9
May 27 '09 at 13:41
source share

In fact, you can do this in C # (by providing To and Do as extension methods to int and IEnumerable<T> respectively):

 1.To(7).Do(Console.WriteLine); 

SmallTalk forever!

+9
May 27 '09 at 15:25
source share

I think that foreach + Enumerable.Range is less error prone (you have less control and less ways to do it wrong, for example, decreasing the index inside the body so that the loop never ends, etc.)

The readability problem is related to the semantics of the range function, which can change from one language to another (for example, if only one parameter is specified, it will start with 0 or 1, or this end is included or excluded, or is the second parameter count instead of the final value).

In terms of performance, I think that the compiler should be smart enough to optimize both loops so that they run at the same speed even with large ranges (I believe Range does not create a collection, but of course an iterator).

+6
May 27 '09 at 14:13
source share

I would like to have the syntax of some other languages ​​like Python, Haskell, etc.

 // Write the numbers 1 thru 7 foreach (int index in [1..7]) { Console.WriteLine(index); } 

Fortunately, we now have F # :)

As for C #, I have to stick with the Enumerable.Range method.

+6
May 30 '09 at
source share

I like the idea. This is very similar to Python. Here is my multi-line version:

 static class Extensions { public static IEnumerable<int> To(this int from, int to, int step = 1) { if (step == 0) throw new ArgumentOutOfRangeException("step", "step cannot be zero"); // stop if next 'step' reaches or oversteps 'to', in either +/- direction while (!(step > 0 ^ from < to) && from != to) { yield return from; from += step; } } } 

This works like Python:

  • 0.To(4) β†’ [ 0, 1, 2, 3 ]
  • 4.To(0) β†’ [ 4, 3, 2, 1 ]
  • 4.To(4) β†’ [ ]
  • 7.To(-3, -3) β†’ [ 7, 4, 1, -2 ]
+6
Oct 10 '12 at 10:07
source share

@Luke: I redefined your To() extension method and used the Enumerable.Range() method for this. This way it gets a little shorter and uses as much infrastructure as possible provided to us by .NET:

 public static IEnumerable<int> To(this int from, int to) { return from < to ? Enumerable.Range(from, to - from + 1) : Enumerable.Range(to, from - to + 1).Reverse(); } 
+5
Dec 21 '09 at 16:29
source share

I think Range is useful for working with some inline range:

 var squares = Enumerable.Range(1, 7).Select(i => i * i); 

You can every time. Converting to a list is required, but keeps things compact when you want.

 Enumerable.Range(1, 7).ToList().ForEach(i => Console.WriteLine(i)); 

But besides this, I would use the traditional one for the loop.

+5
May 29 '14 at 15:20
source share

I am sure that everyone has their own personal preferences (many will prefer later, because they are familiar with almost all programming languages), but I like you and begin to love foreach more and more, especially now that you can define a range.

+3
May 27 '09 at 1:39
source share

I suggest that there may be scenarios where Enumerable.Range(index, count) clearer when processing expressions for parameters, especially if some of the values ​​in this expression change in a loop. In the case of for expression will be evaluated based on the state after the current iteration, while Enumerable.Range() is evaluated up.

Other than that, I agree that sticking with for will usually be better (more familiar / readable for more people ... readable is very important in the code that needs to be supported).

+2
May 27 '09 at
source share

In my opinion, the Enumerable.Range() method is more declarative. New and unfamiliar to people? Sure. But I think this declarative approach offers the same benefits as most other LINQ language features.

+2
Mar 19 '12 at 10:30
source share

I agree that in many (or even in most cases) foreach much more readable than the standard for -loop when iterates through a collection. However, your choice of using Enumerable.Range(index, count) not a convincing example of a foreach value for.

For a simple range, starting at 1, Enumerable.Range(index, count) looks quite readable. However, if a range starts at a different index, it becomes less readable, because you must correctly execute index + count - 1 to determine what the last element will be. For example...

 // Write the numbers 2 thru 8 foreach (var index in Enumerable.Range( 2, 7 )) { Console.WriteLine(index); } 

In this case, I prefer the second example.

 // Write the numbers 2 thru 8 for (int index = 2; index <= 8; index++) { Console.WriteLine(index); } 
+1
May 27 '09 at 14:08
source share
Strictly speaking, you are using enumeration incorrectly.

The enumerator provides the means to access all the objects in the container one by one, but it does not guarantee order.

It is good to use an enumeration to find the largest number in an array. If you use it to find, say, the first nonzero element, you rely on implementation details that you should not be aware of. In your example, order seems important to you.

Change I am wrong. As Luke noted (see Comments), one can hope for order when listing an array in C #. This is different from, for example, using "for in" to enumerate an array in Javascript.

+1
May 27 '09 at 16:26
source share

How to use the new syntax today

Because of this question, I tried some things to come up with a good syntax without waiting for first-class language support. Here is what I have:

 using static Enumerizer; // prints: 0 1 2 3 4 5 6 7 8 9 foreach (int i in 0 <= i < 10) Console.Write(i + " "); 

Not the difference between <= and < .

I also created a proof of concept of storage on GitHub with even more functionality (reverse iteration, custom step size).

A minimal and very limited implementation of the above loop would look something like this:

 public readonly struct Enumerizer { public static readonly Enumerizer i = default; public Enumerizer(int start) => Start = start; public readonly int Start; public static Enumerizer operator <(int start, Enumerizer _) => new Enumerizer(start); public static Enumerizer operator >(int _, Enumerizer __) => throw new NotImplementedException(); public static IEnumerable<int> operator <=(Enumerizer start, int end) { for (int i = start.Start; i < end; i++) yield return i; } public static IEnumerable<int> operator >=(Enumerizer _, int __) => throw new NotImplementedException(); } 
+1
03 Sep '19 at 17:39
source share

I like the foreach + Enumerable.Range approach, and I sometimes use it.

 // does anyone object to the new style over the traditional style? foreach (var index in Enumerable.Range(1, 7)) 

I object to the abuse of var in your proposal. I appreciate var , but hell just write int in this case! ;-)

0
May 27 '09 at 14:28
source share



All Articles