Yield – Para que serve?

Talvez você nunca tenha ouvido falar desta palavra – pelo menos no C#, pois ela também está presente no mercado financeiro – , mas acredite, ela está presente no hall de palavras reservadas do C# desde a versão 2.0. Mas para que ela serve? O uso do Yield faz com que o compilador gere um Enumerable no momento do retorno, assim sendo, poderá ser utilizada de duas formas:

  1. yield return <expressão> ou,
  2. yield break.

Uma iteração feita através do yield, faz com que o método que retorna o valor devolva o controle para o método que fez a chamada, este por sua vez executa alguma ação na iteração atual e ao iterar novamente no método anterior (iteração com yield), este continua exatamente de onde parou, como se fosse uma máquina de estado, armazenando sempre o estado anterior da ação, neste caso, o ponto correto de onde a iteração devolveu o controle.

Quando usar?

A utilização do yield se encaixa perfeitamente e dois cenários, iterações customizadas – onde desejamos filtrar alguns dados de uma coleção – e iterações onde precisamos manter o valor anterior de um resultado para uma utilização posterior – Stateful.

Filtrando valores com yield

No código abaixo temos uma iteração sobre um IEnumerable retornado pelo método GetCustomValues.

foreach (var number in GetCustomValues())
   {
       System.Console.WriteLine(number);
   }

O método GetCustomValues itera sobre uma sequência numérica onde seleciona apenas os valores maiores que 5 em um total de 10. Essa seleção é adicionada em uma nova lista chamada myList que armazena os valores selecionados e ao final devolve o resultado, um exemplo bem simples, mas também comum em muitas de nossas tarefas.

static IEnumerable<int> GetCustomValues()
        {
            List<int> myList = new List<int>();

            for (int i = 0; i < 10; i++)
            {
                if (i > 5)
                    myList.Add(i);
            }
            return myList;
        }

Faremos algumas modificações no método GetCustomValues para fazer a utilização do yield.

static IEnumerable<int> GetCustomValues()
        {
            for (int i = 0; i < 10; i++)
                if (i > 5)
                    yield return i;
        }

Quando a condição for atingida, ( i > 5), o método devolverá o controle para o método “chamador” no caso a iteração com foreach, onde será exibido o valor, em seguida o controle será devolvido para o método GetCustomValues, onde este continuará de onde parou e retornará o próximo valor. Essa alternância de controles continuará até que o último valor seja atingido.

Portanto temos um código mais enxuto e que utiliza menos memória, pois não há uma instância (myList) sendo preenchida com os valores desejados, estes agora são retornados diretamente à quem os chamou.

Mantendo o estado de uma iteração

Este segundo cenário de utilização do yield, mostrará como podemos iterar em uma sequência numérica e obter um valor sempre a partir do valor anterior. Isso é possível porque como já comentado, o yield funciona como uma máquina de estado mantendo sempre o estado da última operação realizada.

Um exemplo prático dessa característica do yield está na própria documentação da Microsoft, é a função Power.

class Program
    {
        static void Main(string[] args)
        {
            foreach (var value in Power(2, 4))
            {
                System.Console.WriteLine(value);
            }

            Console.ReadKey();
        }

        static IEnumerable<int> Power(int number, int exponent)
        {
            int result = 1;
            for (int i = 1; i <= exponent; i++)
            {
                yield return result *= number;
            }
        }
    }

A mágica ocorre na variável result, após esta ter sido iniciada com o valor 1, ela passa a receber o resultado na linha 18, onde há o retorno. Na próxima vez que que a função for chamada, o conteúdo da variável result não será mais 1, será o último total armazenado, e assim seguirá até o fim da iteração.

Na sequência de imagens abaixo, ficará mais fácil compreender.

A imagem abaixo mostra o início da primeira iteração, onde a variável result é iniciada com o valor 1 e na linha de retorno já recebe um valor.

Imagem 1 – Primeira iteração.

Na sequência, podemos ver o início da segunda iteração onde a variável mantém o último valor, apenas lembrando que entre cada iteração, o controle é devolvido para outro método (yield return) e volta em seguida.

Imagem 2 – Segunda iteração.

Uma nova iteração em andamento, sempre armazenando o valor anterior.

Imagem 3 – Terceira iteração.

Preparei também uma versão não recursiva da famosa sequência de Fibonacci utilizando o yield.

static IEnumerable<int> Fibonacci(int size)
    {
        int current = -1, fib = 0, next = 1;

        for (int i = 1; i <= size; i++)
        {
            fib = current + next;
            current = next;
            next = fib;
            yield return fib;
        }
    }

Como qualquer coisa em computação, sempre há os cenários ideais e aqueles nem tanto – propósitos de cada ferramenta – . Portanto, estude, compreenda e faça bom uso.

“Para quem só conhece o martelo como ferramenta, parafuso é prego”. Não sei a origem desta frase, mas ela é sensacional.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s