80

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'が小さいのでパフォーマンスが問題ではないと仮定すると、従来のスタイルよりも新しいスタイルに反対する人はいますか?

4

18 に答える 18

59

Range後者の「最小から最大」の形式は、この目的のための「最小数」のスタイルよりもはるかに明確であることがわかります。また、このように、速くも短くもなく、馴染みがなく、明らかに明確でもない標準からこのような変更を加えることは、実際には良い習慣ではないと思います。

そうは言っても、私は一般的な考えに反対していません。次のような構文を思いついた場合は、ループforeach (int x from 1 to 8)よりも改善されることに同意するでしょう。forただし、Enumerable.Rangeかなり不格好です。

于 2009-05-27T14:00:53.817 に答える
46

これはただの楽しみです。(私は標準の " 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);
}
于 2009-05-27T15:24:34.647 に答える
27

C#6.0では、

using static System.Linq.Enumerable;

あなたはそれを単純化することができます

foreach (var index in Range(1, 7))
{
    Console.WriteLine(index);
}
于 2016-04-13T07:27:42.810 に答える
12

実際には、C#でこれを行うことができます(とをそれぞれに拡張メソッドとして提供するToことによりDo)。intIEnumerable<T>

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

SmallTalkは永遠に!

于 2009-05-27T15:25:13.023 に答える
9

私はその考えが好きです。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 ]
于 2012-10-10T10:07:17.390 に答える
7

すでに解決されている問題への非常に長いアプローチのようです。Enumerable.Range背後には、実際には必要のないステートマシン全体があります。

従来の形式は開発の基本であり、すべての人に馴染みがあります。私はあなたの新しいスタイルに何の利点も本当に見ていません。

于 2009-05-27T13:41:07.587 に答える
7

foreach + Enumerable.Rangeは、エラーが発生しにくいと思います(ループが終了しないように本体内のインデックスを減らすなど、制御が少なく、間違った方法が少なくなります)。

読みやすさの問題は、ある言語から別の言語に変更できるRange関数のセマンティクスに関するものです(たとえば、1つのパラメーターだけが指定された場合、0または1から始まるか、終了が含まれるか除外されるか、2番目のパラメーターが終了ではなくカウントになります)価値)。

パフォーマンスに関しては、コンパイラーは両方のループを最適化して、範囲が広い場合でも同じ速度で実行できるようにする必要があると思います(Rangeはコレクションを作成しないと思いますが、もちろんイテレーターです)。

于 2009-05-27T14:13:00.563 に答える
6

Python、Haskellなどの他の言語の構文が欲しいのですが。

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

幸いなことに、F#を取得しました:)

C#については、この方法に固執する必要がありますEnumerable.Range

于 2009-05-30T00:08:13.733 に答える
6

Rangeは、いくつかの範囲をインラインで操作するのに役立つと思います。

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

あなたはそれぞれオーバーすることができます。リストに変換する必要がありますが、それが必要な場合はコンパクトに保ちます。

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

しかし、このようなもの以外は、従来のforループを使用します。

于 2014-05-29T15:20:55.673 に答える
5

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();
}
于 2009-12-21T16:29:32.773 に答える
4

今日の新しい構文の使い方

この質問のために、私は一流の言語サポートを待たずに素晴らしい構文を思い付くためにいくつかのことを試みました。これが私が持っているものです:

using static Enumerizer;

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

との違いではありませ<=<

また、さらに多くの機能(逆反復、カスタムステップサイズ)を備えた概念実証リポジトリを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();
}
于 2019-09-03T17:39:49.450 に答える
3

誰もが個人的な好みを持っていると確信しています(ほとんどすべてのプログラミング言語に精通しているという理由だけで、多くの人は後者を好むでしょう)が、私はあなたのようであり、特に範囲を定義できるようになった今、foreachがますます好きになり始めています。

于 2009-05-27T13:39:57.667 に答える
3

私の意見では、そのEnumerable.Range()方法はより宣言的です。新しくて人になじみがない?そうです。しかし、この宣言型アプローチは、他のほとんどのLINQ関連の言語機能と同じ利点をもたらすと思います。

于 2012-03-19T10:30:01.193 に答える
2

Enumerable.Range(index, count)特にその式の値の一部がループ内で変更された場合、パラメーターの式を処理するときに、より明確なシナリオが存在する可能性があると思います。式の場合for、現在の反復後の状態に基づいて評価されますが、事前Enumerable.Range()に評価されます。

それ以外は、for通常は固執する方が良いと思います(より多くの人にとってより親しみやすく/読みやすい...読み取り可能は、維持する必要のあるコードの非常に重要な値です)。

于 2009-05-27T13:56:20.223 に答える
2

多くの場合(またはほとんどの場合)、コレクションを単純に反復処理する場合foreach、標準のループよりもはるかに読みやすいことに同意します。forただし、使用の選択は、overforEnumerable.Range(index, count)の価値の強力な例ではありませんforeach

から始まる単純な範囲については、1, Enumerable.Range(index, count)かなり読みやすいように見えます。index + count - 1ただし、範囲が別のインデックスで始まる場合は、最後の要素を決定するために適切に実行する必要があるため、読みにくくなります。たとえば…</p>

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

この場合、私は2番目の例を非常に好みます。

// Write the numbers 2 thru 8
for (int index = 2; index <= 8; index++)
{
    Console.WriteLine(index);
}
于 2009-05-27T14:08:56.470 に答える
1

私は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この場合は書いてください!;-)

于 2009-05-27T14:28:22.187 に答える
1

厳密に言えば、列挙を誤用します。

列挙子は、コンテナ内のすべてのオブジェクトに1つずつアクセスする手段を提供しますが、順序を保証するものではありません。

列挙型を使用して、配列内の最大数を見つけることは問題ありません。たとえば、最初のゼロ以外の要素を見つけるためにそれを使用している場合は、知らないはずの実装の詳細に依存しています。あなたの例では、順序はあなたにとって重要であるように思われます。

編集:私は間違っています。Lukeが指摘したように(コメントを参照)、C#で配列を列挙するときは、順序に依存するのが安全です。これは、たとえば、Javascriptで配列を列挙するために「forin」を使用することとは異なります。

于 2009-05-27T16:26:01.513 に答える
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

このアプローチの良いところは、名前によって、終わりが含まれているかどうかを正確に知っていることです。

于 2021-01-10T05:24:30.547 に答える