yield (C# リファレンス)

ステートメントで yieldyieldを使用した場合、メソッド、演算子、または get アクセサーが反復子であることを示します。 yield を使用して反復子を定義すると、カスタム コレクション型の IEnumerator<T> および IEnumerable パターンを実装するときに明示的な余分なクラス (列挙の状態を保持するクラス。たとえば IEnumerator を参照) が不要になります。

yield ステートメントの 2 つの形式を次の例に示します。

yield return <expression>;
yield break;

Remarks

各要素を 1 つずつ返すには、yield return ステートメントを使用します。

Iterator メソッドから返されるシーケンスを、foreach ステートメントまたは LINQ クエリを使って使用することができます。 foreach ループの各イテレーションは、Iterator メソッドを呼び出します。 yield return ステートメントが Iterator メソッドに到達すると、expression が返され、コードの現在の位置が保持されます。 次回、Iterator 関数が呼び出されると、この位置から実行が再開されます。

反復子から System.Collections.Generic.IAsyncEnumerable<T> が返された場合は、System.Collections.Generic.IAsyncEnumerable<T> ステートメントを使用して、そのシーケンスを非同期的に使用できます。 ループの反復は、foreach ステートメントに似ています。 違いは、次の要素の式が返される前に、各反復が非同期操作に対して中断される可能性がある点です。

yield break ステートメントを使用すると、反復を終了できます。

反復子について詳しくは、「Iterators」をご覧ください。

Iterator メソッドと get アクセサー

反復子の宣言は、次の要件を満たす必要があります。

yield または IEnumerable を返す反復子の IEnumerator 型は object です。 反復子から IEnumerable<T> または IEnumerator<T> が返される場合は、yield return ステートメント内の式の型から、ジェネリック型パラメーターへの暗黙的な変換が存在する必要があります。

yield return または yield break ステートメントを以下に含めることはできません。

例外処理

yield return ステートメントは、try-catch ブロックに配置することはできません。 yield return ステートメントは、try-finally ステートメントの try ブロックに配置できます。

yield break ステートメントは、try ブロックまたは catch ブロックには配置できますが、finally ブロックには配置できません。

foreach または await foreach の本体 (Iterator メソッドの外部) で例外がスローされた場合、Iterator メソッドの finally ブロックが実行されます。

技術的な実装

次のコードは、iterator メソッドから IEnumerable<string> を返した後、要素を反復処理します。

IEnumerable<string> elements = MyIteratorMethod();
foreach (string element in elements)
{
   ...
}

MyIteratorMethod への呼び出しでは、メソッドの本体は実行されません。 この呼び出しでは、IEnumerable<string>elements 変数に返されます。

foreach ループの反復処理では、MoveNext について elements メソッドが呼び出されます。 この呼び出しでは、次の MyIteratorMethod ステートメントに到達するまで、yield return の本体が実行されます。 yield return ステートメントによって返される式は、ループ本体による処理に対する element 変数の値だけでなく、IEnumerable<string> である、elementsCurrent プロパティも決定します。

foreach ループの以降の各反復処理では、反復子本体の実行が中断した場所から続行し、yield return ステートメントに到達したときに再度停止します。 iterator メソッドまたは foreach ステートメントの最後に到達すると、yield break ループは完了します。

次のコードは、iterator メソッドから IAsyncEnumerable<string> を返した後、要素を反復処理します。

IAsyncEnumerable<string> elements = MyAsyncIteratorMethod();
await foreach (string element in elements)
{
   // ...
}

await foreach ループの反復処理では、IAsyncEnumerator<T>.MoveNextAsync について elements メソッドが呼び出されます。 MoveNext による System.Threading.Tasks.ValueTask<TResult> の戻りは、次の yield return に達すると完了します。

await foreach ループの以降の各反復処理では、反復子本体の実行が中断した場所から続行し、yield return ステートメントに到達したときに再度停止します。 iterator メソッドまたは await foreach ステートメントの最後に到達すると、yield break ループは完了します。

次の例では、yield return ループ内に for ステートメントが含まれます。 Main メソッド内の foreach ステートメント本体の各反復処理では、Power Iterator 関数が呼び出されます。 Iterator 関数を呼び出すごとに、yield return ステートメントの次の実行に進みます。これは、for ループの次の反復処理で行われます。

Iterator メソッドの戻り値の型は IEnumerable であり、これは反復子インターフェイス型です。 Iterator メソッドが呼び出されると、数値の累乗を含む列挙可能なオブジェクトが返されます。

public class PowersOf2
{
    static void Main()
    {
        // Display powers of 2 up to the exponent of 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }

    public static System.Collections.Generic.IEnumerable<int> Power(int number, int exponent)
    {
        int result = 1;

        for (int i = 0; i < exponent; i++)
        {
            result = result * number;
            yield return result;
        }
    }

    // Output: 2 4 8 16 32 64 128 256
}

次の例は、反復子である get アクセサーを示しています。 この例では、各 yield return ステートメントがユーザー定義クラスのインスタンスを返します。

public static class GalaxyClass
{
    public static void ShowGalaxies()
    {
        var theGalaxies = new Galaxies();
        foreach (Galaxy theGalaxy in theGalaxies.NextGalaxy)
        {
            Debug.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears.ToString());
        }
    }

    public class Galaxies
    {

        public System.Collections.Generic.IEnumerable<Galaxy> NextGalaxy
        {
            get
            {
                yield return new Galaxy { Name = "Tadpole", MegaLightYears = 400 };
                yield return new Galaxy { Name = "Pinwheel", MegaLightYears = 25 };
                yield return new Galaxy { Name = "Milky Way", MegaLightYears = 0 };
                yield return new Galaxy { Name = "Andromeda", MegaLightYears = 3 };
            }
        }
    }

    public class Galaxy
    {
        public String Name { get; set; }
        public int MegaLightYears { get; set; }
    }
}

C# 言語仕様

詳細については、「C# 言語の仕様」を参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。

関連項目