用Enumerable.Range与传统for循环的foreach思考

在C#3.0中,我喜欢这种风格:

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

在传统的循环中:

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

假设'n'很小,所以表演不是问题,有没有人反对传统风格的新风格?

我发现后者的“最小到最大”格式比Range的“最小数量”格式明显得多。 另外,我认为从一个不是更快,更短,更不熟悉,也不明显更清晰的标准这样的改变,并不是一个好的做法。

这就是说,我一般不反对这个想法。 如果你的语法看起来像foreach (int x from 1 to 8)那么我可能会认为这将是对for循环的改进。 但是, Enumerable.Range非常笨重。

这只是为了好玩。 (我只是使用自己的标准“ 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--; } } } 

您也可以添加一个Step扩展方法:

 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); } 

你可以在C#中实际做到这一点(通过在intIEnumerable<T>分别提供ToDo作为扩展方法):

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

SmallTalk永远!

对于已经解决的问题来说,这似乎是一个相当漫长的过程。 在Enumerable范围后面有一个完全不需要的状态机。

传统的格式是发展的基础,也是所有人都熟悉的。 我真的没有看到你的新风格有什么优势。

在C#6.0中使用

 using static System.Linq.Enumerable; 

你可以简化它

 foreach(var index in Range( 1, 7 )) { Console.WriteLine( index ); } 

我认为foreach + Enumerable.Range不太容易出错(你有更less的控制和更less的方法来做错误,比如减less内部的索引,所以循环不会结束等)

可读性问题是关于范围函数语义,可以从一种语言改变到另一种(例如,如果只给定一个参数,它将从0或1开始,或者是包含或排除的结尾,或者是第二个参数,而不是结束值)。

关于性能,我认为编译器应该足够聪明,可以优化两个循环,所以它们以相似的速度执行,即使是大范围(我认为Range不会创build集合,当然也是迭代器)。

我想要有Python,Haskell等其他语言的语法

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

幸运的是,我们现在得到了F#

至于C#,我将不得不坚持使用Enumerable.Range方法。

我有点像这个想法。 这非常像Python。 这是几行我的版本:

 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; } } } 

它像Python一样工作:

  • 0.To(4) – > [0,1,2,3]
  • 4.To(0) – > [ 4.To(0) ]
  • 4.To(4) – > []
  • 7.To(-3, -3) – > [ 7.To(-3, -3) ,-2]

@Luke:我重新实现了To()扩展方法,并使用Enumerable.Range()方法来完成它。 这样,它就会变得更短一些,并尽可能多地使用.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(); } 

我相信每个人都有他们的个人喜好(很多人会喜欢后者,因为它几乎所有的编程语言都很熟悉),但是我喜欢你,越来越喜欢这个foreach,尤其是现在你可以定义一个范围。

我想可能会出现Enumerable.Range(index, count)处理参数expression式的情况,特别是如果expression式中的某些值在循环内被改变的话。 在expression式将根据当前迭代之后的状态进行评估的情况下, Enumerable.Range()将在前面进行评估。

除此之外,我同意坚持使用通常会更好(对更多人来说更熟悉/可读)可读性是需要维护的代码中非常重要的值。

在我看来,Enumerable.Range()方法更具说明性。 新的和陌生的人? 当然。 但是我认为这种声明式方法与大多数其他与LINQ相关的语言特性具有相同的优点。

我认为Range对于一些内联范围的工作很有用:

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

你可以每一个结束。 需要转换到列表,但保持紧凑时,这是你想要的。

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

但除了这样的事情,我会使用传统的循环。

我同意在很多(或者甚至大多数情况下)foreach比标准的for循环更可读,当迭代一个集合。 但是,您使用Enumerable.Range(index,count)的select不是foreach的值的一个强有力的例子。

对于从1开始的简单范围,Enumerable.Range(index,count)看起来非常可读。 但是,如果范围从一个不同的索引开始,那么它的可读性会变差,因为您必须正确执行index + count – 1来确定最后一个元素是什么。 例如…

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

在这种情况下,我更喜欢第二个例子。

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

严格来说,你滥用枚举。

枚举器提供了一个一个地访问容器中所有对象的方法,但不能保证顺序。

使用枚举可以find数组中最大的数。 如果您正在使用它来查找第一个非零元素,那么您将依赖于您不应该了解的实现细节。 在你的例子中,顺序似乎对你很重要。

编辑 :我错了。 正如Luke指出的那样(请参阅注释),在C#中枚举数组时,依靠顺序是安全的。 这与例如使用“for in”枚举Javascript中的数组不同。

我喜欢foreach + Enumerable.Range方法并且有时使用它。

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

我反对你提案中的滥用滥用。 我欣赏var,但是,该死的,只是在这种情况下写int ! 😉