Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Use a yield instrução em um iterador para fornecer o próximo valor ou sinalizar o final de uma iteração. A instrução yield tem as duas seguintes formas:
yield return: para fornecer o próximo valor na iteração, como mostra o seguinte exemplo: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 seguinte exemplo: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 é concluída quando o controle atinge o final de um iterador.
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para recursos em visualizações públicas para a próxima versão do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
Nos exemplos anteriores, o tipo de retorno de iteradores é IEnumerable<T>. Em casos nãogenericos, use IEnumerable como o tipo de retorno de um iterador. Você também pode usar IAsyncEnumerable<T> como tipo de retorno de um iterador. Isso torna o iterador assíncrono. Use a instruçãoawait foreach para iterar sobre o resultado do iterador, como mostra o seguinte exemplo:
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 podem ser o tipo de retorno de um iterador. Use esses tipos de retorno quando você implementar o método GetEnumerator nos seguintes cenários:
Você projeta o tipo que implementa a interface IEnumerable<T> ou IEnumerable.
Adicione uma instância ou método de extensão
GetEnumeratorpara permitir a iteração sobre a instância do tipo com aforeachdeclaraçã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; } }
Você não pode usar instruções yield em:
- métodos com os parâmetros in, ref ou out.
- expressões lambda e métodos anônimos
-
blocos inseguros. Antes do C# 13,
yieldera inválido em qualquer método com um blocounsafe. A partir do C# 13, você pode usaryieldem métodos com blocosunsafe, mas não no blocounsafe. -
yield returneyield breaknão pode ser usado em blocos catch e finally , ou em blocos try com um bloco correspondentecatch. Asyield returninstruções andyield breakpodem ser usadas em umtrybloco semcatchblocos, apenas umfinallybloco.
using instruções em iteradores
Você pode usar using instruções em métodos de iterador. Como using as instruções são compiladas em try blocos com finally cláusulas (e sem catch blocos), elas funcionam corretamente com iteradores. Os recursos descartáveis são gerenciados corretamente durante a execução do iterador:
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.");
}
Como mostra o exemplo anterior, o recurso adquirido na using instrução permanece disponível durante toda a execução do iterador, mesmo quando o iterador suspende e retoma a execução em yield return instruções. O recurso é descartado quando o iterador é concluído (atingindo o final ou via yield break) ou quando o iterador em si é descartado (por exemplo, quando o chamador sai da enumeração mais cedo).
Execução de um iterador
Chamar um iterador não o 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, o iterador é executado até que a primeira yield return instrução seja alcançada. Em seguida, a execução do iterador é suspensa e o chamador obtém o primeiro valor de iteração e o processa. Em cada iteração subsequente, a execução do 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 atingida. A iteração é concluída quando o controle atinge o final de um iterador ou uma instrução yield break.
Especificação da linguagem C#
Para saber mais, confira a seção A instrução yield na Especificação da linguagem C#.