Muitas pessoas ainda não estão usando o poder deste recurso chamado yield. Porque ? Talvez porque muitas pessoas ainda não tenham entendido a utilização deste recurso.

Neste blog tentarei, usando um exemplo bem simples, mostrar um pouquinho deste podereoso, porem ainda não muito utilizado ou entendido recurso introduzido no .net framework 2.0.

Sempre que usamos um "foreach", necessitamos usar um tipo que implemente a interface IEnumerable ou IEnumerable<T>, todos os arrays e muitas coleções (ArrayList, List<T>) implementam IEnumerable, esse é o motivo no qual podemos usar esses tipos num "foreach"

Exemplo:

IEnumerable<int> numbers = new int[] { 11, 29, 33, 7, 54, 65, 16, 80 };
foreach (int i in numbers) { Console.WriteLine(i); }

Nesse exemplo vemos que para o foreach funcionar corretamente o IEnumerable foi criado e preenchido com todos os items, para então ser possivel retornar essa colecão de informação.

Agora imagine um cenário no qual em caso você encontre uma determinada informação seu trabalho termina e o restante das informações nao llhe servem de nada. Nesse cenário vamos simular que para gerar essa lista é necessario operações longas que tomam tempo.

Por exemplo: Numa lista de numeros gerados numa operação de potencia, você gostaria de saber qual o indice que o número 32 se encontra.

static void Main(string[] args)

{

Stopwatch stopWatchTimer = Stopwatch.StartNew();

Console.WriteLine("Inicio da Demo");

int index = 0;

foreach (int i in Potencia(2, 10))

{

if (i >= 32)

{

Console.WriteLine("Numero 32 foi encontrado no indice: " + index);

break;

}

index++;

}

stopWatchTimer.Stop();

Console.WriteLine("Tempo total gasto: " + stopWatchTimer.Elapsed);

Console.Read();

}

public static List<int> Potencia(int num, int potencia)

{

List<int> lista = new List<int>();

int result = 1;

for (int i = 0; i < potencia; i ++ )

{

Thread.Sleep(1000); // Simula uma operação cara

result = result * num;

lista.Add(result);

}

return lista;

}

Neste exemplo sabemos que teremos 10 operação uma vez que estamos elevando 2 a 10 (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024), cada operação levaria nessa simulação 1 segundo (totalizando 10 segundos), porem queremos saber qual é o indice do numero 32, como optimizar meu código ? Resposta:  Usando a magica do yield.

No exemplo acima teriamos o seguinte output:

Inicio da Demo
Numero 32 foi encontrado no indice: 4
Tempo total gasto: 00:00:10.0037011

 

Vamos alterar nossa função para utilizar yield:

public static IEnumerable Potencia(int num, int potencia)

{

int result = 1;

for (int i = 0; i < potencia; i++)

{

Thread.Sleep(1000); // Simula uma operação cara

result = result * num;

yield return result;

}

}

Ao executar novamente nosso código:

Inicio da Demo
Numero 32 foi encontrado no indice: 4
Tempo total gasto: 00:00:05.0046293

A execução do código passou de 10.0037011 segundos para 05.0046293 segundos. Um ganho por volta de 50% do tempo.

 

Entendendo a diferença entre as execuções:

Implementação normal

Implementação Yield
  1. Chamador chama a função
  2. Função executa e retorna a lista
  3. Chamador usa a lista
  1. Chamador chama a função
  2. Chamador requisita o item
  3. Próximo item é retornado
  4. Volta para o passo #2

 

Conclusão

Yield pode tornar seu código muito mais eficiente. Este recurso esta disponivel desde o .NET 2.0, não há mais motivos para você não estar usando ainda.

clique aqui para ler um outro post (em inglês) muito interessante sobre yield.

Forte abraço.