Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Use a yield instrução num 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.
A referência da linguagem C# documenta a versão mais recentemente lançada da linguagem C#. Contém também documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento linguístico.
A documentação identifica qualquer funcionalidade introduzida pela primeira vez nas últimas três versões da língua ou em pré-visualizações públicas atuais.
Sugestão
Para saber quando uma funcionalidade foi introduzida pela primeira vez em C#, consulte o artigo sobre o histórico de versões da linguagem C#.
Nos exemplos anteriores, o tipo de retorno dos iteradores é IEnumerable<T>. Em casos não genéricos, use IEnumerable como 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çãoawait 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
GetEnumeratorpara habilitar a iteração sobre a instância do tipo com aforeachinstruçã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 em, refou fora parâmetros.
- expressões lambda e métodos anônimos.
-
bloqueios inseguros. Antes do C# 13,
yieldera inválido em qualquer método com umunsafebloco. A partir do C# 13, você pode usaryieldmétodos comunsafeblocos, mas não nounsafebloco . -
yield returneyield breaknão pode ser usado em bloqueios de catch e finalmente , nem em bloqueios de ensaio com um bloqueio correspondentecatch. Asyield returninstruções eyield breakpodem ser usadas em umtrybloco semcatchblocos, apenas umfinallybloco.
using instruções em iteradores
Você pode usar using instruções em métodos iteradores. Como using as instruções compilam-se em try blocos com finally cláusulas (e sem blocos catch ), funcionam corretamente com iteradores. Os recursos descartáveis são gerenciados adequadamente durante toda 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 instrução permanece disponível durante toda a using execução do iterador, mesmo quando o iterador suspende e retoma a execução nas yield return instruções. O recurso é descartado quando o iterador é concluído (chegando ao final ou via yield break) ou quando o próprio iterador é 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 seguinte:
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 se começa a iterar sobre o resultado de um iterador, este executa até chegar à primeira yield return instrução. Depois, a execução do iterador é suspensa e o chamador recebe o valor da primeira iteração e processa-o. Em cada iteração subsequente, a execução do iterador retoma 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#.