Thoughts on foreach with Enumerable.Range vs traditional for loop

前端 未结 18 1082
花落未央
花落未央 2021-01-30 06:13

In C# 3.0, I\'m liking this style:

// Write the numbers 1 thru 7
foreach (int index in Enumerable.Range( 1, 7 ))
{
    Console.WriteLine(index);
}
相关标签:
18条回答
  • 2021-01-30 06:53

    Just throwing my hat into the ring.

    I define this...

    namespace CustomRanges {
    
        public record IntRange(int From, int Thru, int step = 1) : IEnumerable<int> {
    
            public IEnumerator<int> GetEnumerator() {
                for (var i = From; i <= Thru; i += step)
                    yield return i;
            }
    
            IEnumerator IEnumerable.GetEnumerator()
                => GetEnumerator();
        };
    
        public static class Definitions {
    
            public static IntRange FromTo(int from, int to, int step = 1)
                => new IntRange(from, to - 1, step);
    
            public static IntRange FromThru(int from, int thru, int step = 1)
                => new IntRange(from, thru, step);
    
            public static IntRange CountFrom(int from, int count)
                => new IntRange(from, from + count - 1);
    
            public static IntRange Count(int count)
                => new IntRange(0, count);
    
            // Add more to suit your needs. For instance, you could add in reversing ranges, etc.
        }
    }
    

    Then anywhere I want to use it, I add this at the top of the file...

    using static CustomRanges.Definitions;
    

    And use it like this...

    foreach(var index in FromTo(1, 4))
        Debug.WriteLine(index);
    // Prints 1, 2, 3
    
    foreach(var index in FromThru(1, 4))
        Debug.WriteLine(index);
    // Prints 1, 2, 3, 4
    
    foreach(var index in FromThru(2, 10, 2))
        Debug.WriteLine(index);
    // Prints 2, 4, 6, 8, 10
    
    foreach(var index in CountFrom(7, 4))
        Debug.WriteLine(index);
    // Prints 7, 8, 9, 10
    
    foreach(var index in Count(5))
        Debug.WriteLine(index);
    // Prints 0, 1, 2, 3, 4
    
    foreach(var _ in Count(4))
        Debug.WriteLine("A");
    // Prints A, A, A, A
    

    The nice thing about this approach is by the names, you know exactly if the end is included or not.

    0 讨论(0)
  • 2021-01-30 06:54

    I'm sure everybody has their personal preferences (many would prefer the later just because it is familiar over almost all programming languages), but I am like you and starting to like the foreach more and more, especially now that you can define a range.

    0 讨论(0)
  • 2021-01-30 06:54

    How to use a new syntax today

    Because of this question I tried out some things to come up with a nice syntax without waiting for first-class language support. Here's 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 repository on GitHub with even more functionality (reversed iteration, custom step size).

    A minimal and very limited implementation of the above loop would look something like 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();
    }
    
    0 讨论(0)
  • 2021-01-30 06:54

    I imagine there could be scenarios where Enumerable.Range(index, count) is clearer when dealing with expressions for the parameters, especially if some of the values in that expression are altered within the loop. In the case of for the expression would be evaluated based on the state after the current iteration, whereas Enumerable.Range() is evaluated up-front.

    Other than that, I'd agree that sticking with for would normally be better (more familiar/readable to more people... readable is a very important value in code that needs to be maintained).

    0 讨论(0)
  • 2021-01-30 06:56

    You can actually do this in C# (by providing To and Do as extension methods on int and IEnumerable<T> respectively):

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

    SmallTalk forever!

    0 讨论(0)
  • 2021-01-30 06:57

    @Luke: I reimplemented your To() extension method and used the Enumerable.Range() method to do it. This way it comes out a little shorter and uses as much infrastructure given to us by .NET as possible:

    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();
    }
    
    0 讨论(0)
提交回复
热议问题