Megosztás a következőn keresztül:


hozamkimutatás – adja meg a következő elemet

Az iterátorbanyieldutasítással adja meg a következő értéket, vagy jelezheti az iteráció végét. Az yield utasítás a következő két űrlapot tartalmazza:

  • yield return: a következő értéket adja meg az iterációban, ahogy az alábbi példa is mutatja:

    foreach (int i in ProduceEvenNumbers(9))
    {
        Console.Write(i);
        Console.Write(" ");
    }
    // Output: 0 2 4 6 8
    
    IEnumerable<int> ProduceEvenNumbers(int upto)
    {
        for (int i = 0; i <= upto; i += 2)
        {
            yield return i;
        }
    }
    
  • yield break: az iteráció befejezésének explicit jelzése, ahogy az alábbi példa is mutatja:

    Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {2, 3, 4, 5, -1, 3, 4})));
    // Output: 2 3 4 5
    
    Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {9, 8, 7})));
    // Output: 9 8 7
    
    IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers)
    {
        foreach (int n in numbers)
        {
            if (n > 0)
            {
                yield return n;
            }
            else
            {
                yield break;
            }
        }
    }
    

    Az iteráció akkor is befejeződik, ha a vezérlő eléri az iterátor végét.

Az előző példákban az iterátorok IEnumerable<T> visszatérési típusa (nemgenerikus esetekben az iterátor visszatérési típusaként használható IEnumerable ). Az iterátor visszatérési típusaként is használható IAsyncEnumerable<T> . Ez egy iterátor aszinkron lesz. await foreach Az utasítás használatával iterálhatja az iterátor eredményét, ahogy az alábbi példa is mutatja:

await foreach (int n in GenerateNumbersAsync(5))
{
    Console.Write(n);
    Console.Write(" ");
}
// Output: 0 2 4 6 8

async IAsyncEnumerable<int> GenerateNumbersAsync(int count)
{
    for (int i = 0; i < count; i++)
    {
        yield return await ProduceNumberAsync(i);
    }
}

async Task<int> ProduceNumberAsync(int seed)
{
    await Task.Delay(1000);
    return 2 * seed;
}

IEnumerator<T> vagy IEnumerator egy iterátor visszatérési típusa is lehet. Ezeket a visszatérési típusokat akkor használja, ha a metódust GetEnumerator a következő forgatókönyvekben implementálja:

  • A megvalósító IEnumerable<T> vagy IEnumerable interfész típust kell megtervezni.

  • Az utasítássalGetEnumerator típuspéldányon, ahogy az alábbi példa is mutatja:

    public static void Example()
    {
        var point = new Point(1, 2, 3);
        foreach (int coordinate in point)
        {
            Console.Write(coordinate);
            Console.Write(" ");
        }
        // Output: 1 2 3
    }
    
    public readonly record struct Point(int X, int Y, int Z)
    {
        public IEnumerator<int> GetEnumerator()
        {
            yield return X;
            yield return Y;
            yield return Z;
        }
    }
    

A következő utasítások nem használhatók yield :

  • metódusok a -ban, hivatkozással a -ra, vagy kimeneti paramétereken.
  • lambda kifejezések és névtelen metódusok.
  • nem biztonságos blokkok. A C# 13 yield előtt érvénytelen volt bármely blokkot unsafe tartalmazó metódusban. A C# 13-tól kezdve blokkokkal rendelkező yield metódusokban használhatóunsafe, blokkokban unsafe azonban nem.
  • yield return és yield break nem használható a fogási és végül a blokkok, vagy próbálja blokkok egy megfelelő catch blokk. Az yield return és yield break az utasítások blokkok nélkültry, csak blokkokban catch használhatókfinally.

using utasítások iterátorokban

Az utasítások iterátori metódusokban is használhatókusing. Mivel using az utasítások záradékokkal (tryés blokkok nélkülfinally) rendelkező blokkokba vannak lefordítvacatch, az iterátorokkal megfelelően működnek. Az eldobható erőforrások megfelelően vannak kezelve az iterátor végrehajtása során:

Console.WriteLine("=== Using in Iterator Example ===");

// Demonstrate that using statements work correctly in iterators
foreach (string line in ReadLinesFromResource())
{
    Console.WriteLine($"Read: {line}");
    // Simulate processing only first two items
    if (line == "Line 2") break;
}

Console.WriteLine("Iteration stopped early - resource should still be disposed.");

static IEnumerable<string> ReadLinesFromResource()
{
    Console.WriteLine("Opening resource...");
    using var resource = new StringWriter(); // Use StringWriter as a simple IDisposable
    resource.WriteLine("Resource initialized");
    
    // These lines would typically come from the resource (e.g., file, database)
    string[] lines = { "Line 1", "Line 2", "Line 3", "Line 4" };
    
    foreach (string line in lines)
    {
        Console.WriteLine($"About to yield: {line}");
        yield return line;
        Console.WriteLine($"Resumed after yielding: {line}");
    }
    
    Console.WriteLine("Iterator completed - using block will dispose resource.");
}

Ahogy az előző példa is mutatja, az utasításban using beszerzett erőforrás az iterátor végrehajtása során is elérhető marad, még akkor is, ha az iterátor felfüggeszti és folytatja a végrehajtást az utasításoknál yield return . Az erőforrást az iterátor befejezésekor (vagy a vég elérésével, vagy a művelet végrehajtásával yield break) vagy az iterátor elvetésekor (például amikor a hívó korán kiesik az enumerálásból).

Iterátor végrehajtása

Az iterátor hívása nem hajtja végre azonnal, ahogy az alábbi példa is mutatja:

var numbers = ProduceEvenNumbers(5);
Console.WriteLine("Caller: about to iterate.");
foreach (int i in numbers)
{
    Console.WriteLine($"Caller: {i}");
}

IEnumerable<int> ProduceEvenNumbers(int upto)
{
    Console.WriteLine("Iterator: start.");
    for (int i = 0; i <= upto; i += 2)
    {
        Console.WriteLine($"Iterator: about to yield {i}");
        yield return i;
        Console.WriteLine($"Iterator: yielded {i}");
    }
    Console.WriteLine("Iterator: end.");
}
// Output:
// Caller: about to iterate.
// Iterator: start.
// Iterator: about to yield 0
// Caller: 0
// Iterator: yielded 0
// Iterator: about to yield 2
// Caller: 2
// Iterator: yielded 2
// Iterator: about to yield 4
// Caller: 4
// Iterator: yielded 4
// Iterator: end.

Ahogy az előző példa is mutatja, amikor egy iterátor eredménye fölött kezd iterálni, a rendszer addig hajt végre egy iterátort, amíg el nem éri az első yield return utasítást. Ezután az iterátor végrehajtása fel lesz függesztve, és a hívó megkapja az első iterációs értéket, és feldolgozza azt. Minden további iteráció esetében az iterátor végrehajtása az előző felfüggesztést okozó utasítás után yield return folytatódik, és a következő yield return utasítás eléréséig folytatódik. Az iteráció akkor fejeződik be, amikor a vezérlő eléri az iterátor vagy egy yield break utasítás végét.

C# nyelvspecifikáció

További információ: A C# nyelv specifikációjának hozamkimutatási szakasza.

Lásd még