「利回りが壊れる」とは C#で行う?


494

私はこの構文をMSDNで見ました:がyield break、それが何をするのかわかりません。誰か知っていますか?


26
それでも、MSDNのドキュメントでは言及していますが、説明はありません。これは、知識に飢えた開発者をいじめるための悪い方法です。
Phil

3
Yield returnを使用すると、バッキングリストを作成する必要がなくなります。つまり、MyList.Add(...)単に行うようにコーディングする必要はありませんyield return ...。ループから途中で抜け出し、使用する仮想バッキングリストを返す必要がある場合yield break;
The Muffin Man

回答:


529

イテレータが終了したことを示します。あなたは考えることができyield breakreturn値を返さない文。

たとえば、関数をイテレータとして定義すると、関数の本体は次のようになります。

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

ループがすべてのサイクルを完了すると、最後の行が実行され、コンソールアプリにメッセージが表示されることに注意してください。

またはこのようにyield break

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

この場合、関数を早く終了したため、最後のステートメントは実行されません。


8
上記の例のbreak代わりになるだけyield breakですか?コンパイラはそれについて文句を言いません。
orad

85
breakこの場合、@ orad simple はループを停止しますが、メソッドの実行を中止しないため、最後の行が実行され、「Wo n't see me text」が実際に表示されます。
DamirZekić14年

5
@DamirZekić回答に、収益よりも利回りブレイクを優先すべき理由と、2つの違いは何ですか?
Bruno Costa

11
@DamirZekićがnullを返し、歩留まりは同じではありません。nullを返す場合NullReferenceExceptionは、IEnumerableの列挙子を取得できますが、yieldブレークでは取得できません(要素のないインスタンスがあります)。
Bruno Costa

6
@BrunoCosta同じメソッドで定期的にリターンしようとすると、コンパイラエラーが発生します。yield return xアラートを使用して、このメソッドにEnumeratorオブジェクトを作成するための構文糖にしたいコンパイラー。この列挙子には、メソッドMoveNext()とプロパティがありますCurrent。MoveNext()はyield returnステートメントまでメソッドを実行し、その値をに変換しCurrentます。次回MoveNextが呼び出されると、そこから実行が続行されます。yield breakCurrentをnullに設定し、この列挙子の終了を通知して、a foreach (var x in myEnum())が終了するようにします。
Wolfzoon、

57

イテレータブロックを終了します(たとえば、IEnumerableに要素がなくなると言います)。


2
[+1]付き-学術的には.NETには反復子はありません。列挙子のみ(一方向、順方向)
Shaun Wilson

@Shaun Wilsonですが、yieldキーワードを使用すると、コレクションを双方向に反復できます。さらに、コレクションの次の要素すべてを一列に
並べる

@monstrは、任意の方法でコレクションを反復処理でき、その必要はありませんyield。私はあなたが間違っていると言っているのではなく、あなたが示唆していることがわかります。ただし、学術的には.NETにはイテレータはなく、列挙子(一方向、前方)のみです。stdc++とは異なり、CTS / CLRで定義された「汎用イテレータフレームワーク」はありません。LINQは、yield return コールバックメソッドを利用する拡張メソッドとのギャップを埋めるのに役立ちますが、これらはファーストクラスの拡張メソッドであり、ファーストクラスのイテレーターではありません。結果IEnumerableは、それ自体が呼び出し元に対して順方向以外に反復することはできません。
Shaun Wilson

29

終わりに達したことをイテレータに通知します。

例として:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

21

yield基本的には、IEnumerable<T>メソッドが(プリエンプティブではなく)協調的にスケジュールされたスレッドと同様に動作するようにします。

yield returnCPUの制御を放棄するために「スケジュール」または「スリープ」関数を呼び出すスレッドのようなものです。スレッドのように、このIEnumerable<T>メソッドは直後の時点でコントロールを取り戻します。すべてのローカル変数は、コントロールが放棄される前と同じ値を持っています。

yield break スレッドがその機能の最後に到達して終了するようなものです。

人々は「ステートマシン」について話しますが、ステートマシンはすべてすべて「スレッド」です。スレッドにはいくつかの状態(つまり、ローカル変数の値)があり、スケジュールされるたびに、新しい状態に到達するためにいくつかのアクションを実行します。重要な点yieldは、これまで使用していたオペレーティングシステムのスレッドとは異なり、それを使用するコードは、反復が手動で進められるか終了されるまで時間内に凍結されるということです。



9

このyield breakステートメントにより、列挙が停止します。実際には、yield break追加のアイテムを返さずに列挙を完了します。

反復子メソッドが反復を停止するには、実際には2つの方法があると考えてください。あるケースでは、メソッドのロジックは、すべてのアイテムを返した後に自然にメソッドを終了できます。次に例を示します。

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

上記の例では、maxCount素数が見つかると、反復子メソッドは当然実行を停止します。

yield break文はイテレータが列挙中止するための別の方法です。これは、列挙を早く抜け出す方法です。上記と同じ方法です。今回は、メソッドが実行できる時間に制限があります。

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

への呼び出しに注意してくださいyield break。実際には、それは列挙を早期に終了しています。

yield break単純なものとは動作が異なることに注意してくださいbreak。上記の例でyield breakは、を呼び出さずにメソッドを終了しますDebug.WriteLine(..)


7

ここでhttp://www.alteridem.net/2007/08/22/the-yield-statement-in-c/は非常に良い例です:

public static IEnumerable <int> Range(int min、int max)
{
   ながら(true)
   {
      if(min> = max)
      {
         歩留まり;
      }
      イールドリターンmin ++;
   }
}

そして説明は、yield breakステートメントがメソッド内でヒットした場合、そのメソッドの実行はリターンなしで停止するということです。結果を出したくない場合は、いくつかの時間的な状況があり、降伏ブレークを使用できます。


5

イールドブレイクは、最後にリターンを言うだけの方法であり、値を返しません

例えば

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

4

「イールドブレークが実際に何をするのか」とは、「どのように機能するのか」です。詳細については、レイモンドチェンのブログを参照してください。https://devblogs.microsoft.com/oldnewthing/20080812-00/?p = 21273

C#イテレータは非常に複雑なコードを生成します。


12
まあ、それらがコンパイルされるコードを気にかけている場合にのみ、それらは複雑になります。それらを使用するコードはかなり単純です。
ロバートロスニー2008年

私はあなたのコメントを含めるように答えを更新しましたので、私が何を意味するかだ@Robert、
トニー・リー

-2

yieldキーワードは、returnキーワードと一緒に使用され、列挙子オブジェクトに値を提供します。yield returnは、返される値を指定します。yield returnステートメントに到達すると、現在の場所が保存されます。反復子が次に呼び出されたときに、この場所から実行が再開されます。

例を使用して意味を説明するには:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

これが繰り返されるときに返される値は、0、2、5です。

この例のカウンター変数はローカル変数であることに注意することが重要です。2の値を返す2回目の反復の後、counterという名前のローカル変数の以前の値である2 を維持しながら、3回目の反復は以前の場所から開始します。


11
あなたは何をyield breakしているのか説明しませんでした
Jay Sullivan

私はyield return実際には複数の値を返すことをサポートしていないと思います。多分それはあなたが実際に意味したものではないかもしれませんが、それは私がそれを読む方法です。
2013年

Sam-実際に複数のyield returnステートメントを持つSampleNumbersメソッドは機能し、イテレータの値はすぐに返され、次の値が要求されると実行が再開されます。私は人々がこのような方法を「収量ブレイク」で終わらせるのを見ましたが、それは不必要です。メソッドの最後に
到達

これが不適切な例である理由は、a yield breakなどの言語レベルの列挙子が含まれていないためです。foreach列挙子を使用すると、yield break実際の値が提供されます。この例は、展開されたループのように見えます。現実の世界ではこのコードを見ることはほとんどありません(確かにいくつかのエッジケースを考えることができます)。また、ここには "反復子"はありません。「イテレータブロック」は、言語仕様の問題として、メソッドを超えて拡張することはできません。どのような実際に戻されていることは「可算」で、以下を参照してください。stackoverflow.com/questions/742497/...
ショーン・ウィルソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.