Demonstração de rendimento - Forneça o próximo elemento
Você usa a yield
instrução em um iterador para fornecer o próximo valor ou sinalizar o fim de uma iteração. A yield
declaração tem as duas seguintes formas:
yield return
: para fornecer o próximo valor na iteração, como mostra o exemplo a seguir: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
: para sinalizar explicitamente o fim da iteração, como mostra o exemplo a seguir: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; } } }
A iteração também termina quando o controle atinge o final de um iterador.
Nos exemplos anteriores, o tipo de retorno de iteradores é IEnumerable<T> (em casos não genéricos, use IEnumerable como o tipo de retorno de um iterador). Você também pode usar IAsyncEnumerable<T> como o tipo de retorno de um iterador. Isso torna um iterador assíncrono. Use a instrução para iterar sobre o await foreach
resultado do iterador, como mostra o exemplo a seguir:
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> ou IEnumerator também pode ser o tipo de retorno de um iterador. Use esses tipos de retorno ao implementar o GetEnumerator
método nos seguintes cenários:
Você projeta o tipo que implementa IEnumerable<T> ou IEnumerable interface.
Você adiciona uma instância ou método de extensão
GetEnumerator
para habilitar a iteração sobre a instância do tipo com aforeach
instrução, como mostra o exemplo a seguir: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; } }
Não é possível usar as yield
instruções em:
- métodos com parâmetros in, ref ou out
- Expressões lambda e métodos anônimos
- bloqueios inseguros. Antes do C# 13,
yield
era inválido em qualquer método com umunsafe
bloco. A partir do C# 13, você pode usaryield
métodos comunsafe
blocos, mas não nounsafe
bloco . yield return
eyield break
não pode ser usado em blocos catch e , finalmente , ou em blocos try com um bloco correspondentecatch
. Asyield return
instruções eyield break
podem ser usadas em umtry
bloco semcatch
blocos, apenas umfinally
bloco.
Execução de um iterador
A chamada de um iterador não a executa imediatamente, como mostra o exemplo a seguir:
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.
Como mostra o exemplo anterior, quando você começa a iterar sobre o resultado de um iterador, um iterador é executado até que a primeira yield return
instrução seja atingida. Em seguida, a execução de um iterador é suspensa e o chamador obtém o primeiro valor de iteração e o processa. Em cada iteração subsequente, a execução de um iterador é retomada após a yield return
instrução que causou a suspensão anterior e continua até que a próxima yield return
instrução seja alcançada. A iteração é concluída quando o controle atinge o final de um iterador ou de uma yield break
instrução.
Especificação da linguagem C#
Para obter mais informações, consulte a seção A instrução de rendimento da especificação da linguagem C#.