Udostępnij za pośrednictwem


instrukcja yield — podaj następny element

Instrukcja jest używana yield w iteratorze, aby podać następną wartość lub zasygnalizować koniec iteracji. Instrukcja yield ma dwie następujące formy:

  • yield return: aby podać następną wartość w iteracji, jak pokazano w poniższym przykładzie:

    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: aby jawnie zasygnalizować koniec iteracji, jak pokazano w poniższym przykładzie:

    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;
            }
        }
    }
    

    Iteracja kończy się również po osiągnięciu końca iteratora kontrolki.

W poprzednich przykładach zwracany typ iteratorów to IEnumerable<T> (w niegenerycznych przypadkach jako IEnumerable typ zwracany iteratora). Można również użyć IAsyncEnumerable<T> jako zwracanego typu iteratora. To sprawia, że iterator jest asynchroniczny. Użyj instrukcji await foreach , aby iterować wynik iteratora, jak pokazano w poniższym przykładzie:

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> lub IEnumerator może być również zwracanym typem iteratora. Użyj tych typów zwracanych podczas implementowania GetEnumerator metody w następujących scenariuszach:

  • Projektujesz typ implementujący IEnumerable<T> lub IEnumerable interfejs.

  • Możesz dodać wystąpienie lub metodę rozszerzeniaGetEnumerator, aby włączyć iterację w wystąpieniu foreach typu za pomocą instrukcji , jak pokazano w poniższym przykładzie:

    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;
        }
    }
    

Nie można używać instrukcji yield w:

  • metody z parametrami in, ref lub out
  • wyrażenia lambda i metody anonimowe
  • niebezpieczne bloki. Przed językiem C# 13 yield był nieprawidłowy w dowolnej metodzie z blokiem unsafe . Począwszy od języka C# 13, można użyć yield metod z blokami unsafe , ale nie w unsafe bloku.
  • yield return i yield break nie można go używać w try, catch i wreszcie bloki.

Wykonywanie iteratora

Wywołanie iteratora nie wykonuje go natychmiast, jak pokazano w poniższym przykładzie:

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.

Jak pokazano w poprzednim przykładzie, gdy zaczniesz iterować wynik iteratora, iterator jest wykonywany do momentu osiągnięcia pierwszej yield return instrukcji. Następnie wykonanie iteratora jest zawieszone, a obiekt wywołujący pobiera pierwszą wartość iteracji i przetwarza ją. W każdej kolejnej iteracji wykonanie iteratora zostanie wznowione po yield return instrukcji, która spowodowała poprzednie zawieszenie i będzie kontynuowana do momentu osiągnięcia kolejnej yield return instrukcji. Iteracji kończy się, gdy kontrolka osiągnie koniec iteratora lub instrukcji yield break .

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz sekcję instrukcji yield specyfikacji języka C#.

Zobacz też