在C#3.0中,我喜欢这种风格:
// Write the numbers 1 thru 7
foreach (int index in Enumerable.Range( 1, 7 ))
{
Console.WriteLine(index);
}
传统的for
循环:
// Write the numbers 1 thru 7
for (int index = 1; index <= 7; index++)
{
Console.WriteLine( index );
}
假设'n'很小,所以性能不是问题,是否有人反对传统风格的新风格?
答案 0 :(得分:46)
我发现后者的“最小到最大”格式比Range
的“最小计数”格式要清晰得多。此外,我认为从这样的规范做出这样的改变并不是一个好习惯,这种改变不是更快,更短,不熟悉,也不是更明确。
那就是说,我并不反对这个想法。如果你找到类似于foreach (int x from 1 to 8)
的语法,那么我可能会同意这比for
循环有所改进。但是,Enumerable.Range
非常笨重。
答案 1 :(得分:37)
这只是为了好玩。 (我自己只使用标准的“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);
}
答案 2 :(得分:12)
在C#6.0中使用
using static System.Linq.Enumerable;
您可以将其简化为
foreach (var index in Range(1, 7))
{
Console.WriteLine(index);
}
答案 3 :(得分:9)
对于已经解决的问题,这似乎是一个冗长的方法。 Enumerable.Range
背后有一个完全不需要的状态机。
传统格式是发展的基础,也是所有人都熟悉的。我觉得你的新风格没什么好处。
答案 4 :(得分:9)
您实际上可以在C#中执行此操作(分别在To
和Do
上提供int
和IEnumerable<T>
作为扩展方法:
1.To(7).Do(Console.WriteLine);
永远的SmallTalk!
答案 5 :(得分:6)
我认为foreach + Enumerable.Range不易出错(你控制较少,错误的方法也少,比如减少体内的索引,这样循环就永远不会结束等等)。
可读性问题是关于Range函数语义,它可以从一种语言变为另一种语言(例如,如果只给出一个参数,它将从0或1开始,或者是包括或排除的结束,还是第二个参数计数而是一个结束值)。
关于性能,我认为编译器应该足够智能以优化两个循环,以便它们以相似的速度执行,即使是大范围(我认为Range不会创建集合,但当然是迭代器)。 / p>
答案 6 :(得分:6)
我有点喜欢这个主意。它非常像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, 3, 2, 1 ]
4.To(4)
→[ ]
7.To(-3, -3)
→[ 7, 4, 1, -2 ]
答案 7 :(得分:5)
我想要其他语言的语法,如Python,Haskell等。
// Write the numbers 1 thru 7
foreach (int index in [1..7])
{
Console.WriteLine(index);
}
幸运的是,我们现在得到了F#:)
对于C#,我必须坚持使用Enumerable.Range
方法。
答案 8 :(得分:5)
@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();
}
答案 9 :(得分:5)
我认为Range对于使用某些范围的内联非常有用:
var squares = Enumerable.Range(1, 7).Select(i => i * i);
你们每个人都可以。需要转换为列表,但在需要的时候保持紧凑。
Enumerable.Range(1, 7).ToList().ForEach(i => Console.WriteLine(i));
但除此之外,我还使用传统的for循环。
答案 10 :(得分:3)
我确信每个人都有自己的个人喜好(很多人会更喜欢后者,因为它对几乎所有的编程语言都很熟悉),但我喜欢你并开始越来越喜欢foreach,尤其是现在你可以定义一个范围。
答案 11 :(得分:2)
我想在处理参数的表达式时可能存在Enumerable.Range(index, count)
更清晰的情况,特别是如果该表达式中的某些值在循环内被更改。在for
的情况下,表达式将根据当前迭代后的状态进行评估,而Enumerable.Range()
则预先进行评估。
除此之外,我同意坚持使用for
通常会更好(对更多人来说更熟悉/可读......可读性是需要维护的代码中非常重要的值)。< / p>
答案 12 :(得分:2)
在我看来,Enumerable.Range()
方式更具说明性。新人和陌生人?当然。但我认为这种声明性方法与大多数其他与LINQ相关的语言功能具有相同的优势。
答案 13 :(得分:1)
严格地说,你滥用枚举。
Enumerator提供了逐个访问容器中所有对象的方法,但它不保证顺序。
使用枚举来查找数组中的最大数字是可以的。如果您使用它来查找第一个非零元素,那么您将依赖于您不应该了解的实现细节。在您的示例中,订单似乎对您很重要。
修改:我错了。正如Luke指出的那样(参见注释),在C#中枚举数组时依赖顺序是安全的。这与例如使用“for in”枚举Javascript中的数组不同。
答案 14 :(得分:1)
由于这个问题,我尝试了一些事情以提供一种不错的语法,而不必等待一流的语言支持。这是我所拥有的:
using static Enumerizer;
// prints: 0 1 2 3 4 5 6 7 8 9
foreach (int i in 0 <= i < 10)
Console.Write(i + " ");
<=
和<
之间没有区别。
我还创建了proof of concept repository on GitHub 具有更多功能(反向迭代,自定义步长)。
上述循环的最小且非常有限的实现如下所示:
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();
}
答案 15 :(得分:1)
我同意在许多(甚至大多数情况下)foreach
比仅仅迭代集合时标准for
循环更可读。但是,您选择使用Enumerable.Range(index, count)
并不是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);
}
答案 16 :(得分:0)
我确实喜欢foreach
+ Enumerable.Range
方法并且有时会使用它。
// does anyone object to the new style over the traditional style?
foreach (var index in Enumerable.Range(1, 7))
我反对您提案中的var
滥用行为。我很感激var
,但是,该死的,在这种情况下只需写int
! ; - )
答案 17 :(得分:0)
只是把我的帽子扔进了戒指。
我定义了这个...
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.
}
}
然后在任何我想使用它的地方,我将它添加到文件顶部...
using static CustomRanges.Definitions;
然后像这样使用它...
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
这种方法的好处在于名称,您可以确切地知道结尾是否包含在内。