O que significa "ruptura de produção"; fazer em c #?

494

Eu já vi essa sintaxe no MSDN:, yield breakmas não sei o que faz. Alguém sabe?

skb
fonte
26
No entanto, a documentação do MSDN menciona, mas não explica. Essa é uma maneira ruim de provocar um desenvolvedor faminto por conhecimento.
28412 Phil
3
O retorno do rendimento elimina a necessidade de uma lista de suporte, ou seja, você não precisa codificar algo como MyList.Add(...)apenas fazer yield return .... Se você precisa sair do loop prematuramente e retornar a lista de apoio virtual que você usaryield break;
The Muffin Man

Respostas:

529

Ele especifica que um iterador chegou ao fim. Você pode pensar yield breakem uma returndeclaração que não retorna um valor.

Por exemplo, se você definir uma função como um iterador, o corpo da função poderá ser assim:

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

Observe que após o loop concluir todos os seus ciclos, a última linha é executada e você verá a mensagem no aplicativo do console.

Ou assim com yield break:

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

Nesse caso, a última instrução nunca é executada porque deixamos a função mais cedo.

Damir Zekić
fonte
8
Poderia ser simplesmente em breakvez de yield breakno seu exemplo acima? O compilador não se queixa disso.
orad 3/06/14
85
@ orad simples breakneste caso interromperia o loop, mas não abortaria a execução do método, assim a última linha seria executada e o "Won't see me text" seria realmente visto.
Damir Zekić
5
@Damir Zekić Você também pode adicionar à sua resposta por que você prefere uma quebra de rendimento sobre o retorno e quais são as diferenças entre os dois?
Bruno Costa
11
@ DamirZekić retornando nulo e quebra de rendimento não é o mesmo. Se você retornar nulo, poderá NullReferenceExceptionobter o enumerador de IEnumerable, enquanto que com a quebra de rendimento, não o fará (há uma instância sem elementos).
Bruno Costa
6
@BrunoCosta Tentar obter retornos regulares no mesmo método gera um erro do compilador. Usando yield return xalertas no compilador, você deseja que esse método seja um açúcar sintático para a criação de um Enumeratorobjeto. Este enumerador tem método MoveNext()e propriedade Current. MoveNext () executa o método até uma yield returninstrução e transforma esse valor em Current. Na próxima vez que MoveNext for chamado, a execução continuará a partir daí. yield breakdefine Current como null, sinalizando o final desse enumerador, para que umforeach (var x in myEnum()) termine.
Wolfzoon
57

Termina um bloco iterador (por exemplo, diz que não há mais elementos no IEnumerable).

Brian
fonte
2
com [+1] - embora academicamente não haja iteradores no .NET. somente enumeradores (uma direção, frente).
Shaun Wilson
@Shaun Wilson, mas com yieldpalavra-chave que você pode iterar coleção em ambas as direções, além disso você pode tomar cada elemento seguinte da coleção não em uma fileira
monstr
@monstr, você pode iterar a coleção de qualquer maneira e não precisa yieldfazê-lo. não estou dizendo que você está errado, entendo o que você está sugerindo; mas academicamente não há iteradores no .NET, apenas enumeradores (uma direção, encaminhamento) - diferentemente do stdc ++, não há uma "estrutura genérica de iterador" definida no CTS / CLR. O LINQ ajuda a fechar a lacuna com métodos de extensão que utilizam yield return e também métodos de retorno de chamada , mas são métodos de extensão de primeira classe, não iteradores de primeira classe. o resultado IEnumerablenão pode, por si só, repetir em outra direção que não seja o redirecionamento do chamador.
Shaun Wilson
29

Diz ao iterador que chegou ao fim.

Como um exemplo:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}
Armadilha
fonte
21

yieldbasicamente faz com que um IEnumerable<T>método se comporte de maneira semelhante a um encadeamento agendado cooperativamente (em oposição a preventivamente).

yield returné como um thread que chama uma função "agendamento" ou "suspensão" para abrir mão do controle da CPU. Assim como um encadeamento, o IEnumerable<T>método recupera os controles no ponto imediatamente depois, com todas as variáveis ​​locais tendo os mesmos valores que tinham antes do controle ser liberado.

yield break é como um thread que chega ao fim de sua função e termina.

As pessoas falam sobre uma "máquina de estado", mas uma máquina de estado é tudo um "fio" realmente é. Um encadeamento possui algum estado (ou seja, valores de variáveis ​​locais) e, toda vez que é agendado, ele executa algumas ações para alcançar um novo estado. O ponto principal yieldé que, diferentemente dos threads do sistema operacional a que estamos acostumados, o código que o utiliza é congelado no tempo até que a iteração seja avançada ou finalizada manualmente.


fonte
9

A yield breakinstrução faz com que a enumeração pare. Com efeito, yield breakconclui a enumeração sem retornar nenhum item adicional.

Considere que, na verdade, existem duas maneiras pelas quais um método iterador pode parar de iterar. Em um caso, a lógica do método poderia naturalmente sair do método depois de retornar todos os itens. Aqui está um exemplo:

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

No exemplo acima, o método do iterador interromperá naturalmente a execução após a localização dos maxCountprimos.

A yield breakdeclaração é outra maneira de o iterador parar de enumerar. É uma maneira de interromper a enumeração mais cedo. Aqui está o mesmo método acima. Desta vez, o método tem um limite para a quantidade de tempo que o método pode executar.

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

Observe a chamada para yield break. Com efeito, está saindo da enumeração mais cedo.

Observe também que o yield breaktrabalho funciona de maneira diferente do que apenas uma planície break. No exemplo acima, yield breaksai do método sem fazer a chamada para Debug.WriteLine(..).

Wallace Kelly
fonte
7

Aqui http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ é um exemplo muito bom:

público estático IEnumerable <int> Range (int min, int max)
{
   enquanto (verdadeiro)
   {
      if (min> = max)
      {
         quebra de rendimento;
      }
      retorno de rendimento min ++;
   }
}

e explicação, que se uma yield breakinstrução for atingida em um método, a execução desse método será interrompida sem retorno. Existem algumas situações em que, quando você não deseja dar nenhum resultado, pode usar a quebra de rendimento.

Towhid
fonte
5

quebra de rendimento é apenas uma maneira de dizer retorno pela última vez e não retornar nenhum valor

por exemplo

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }
John ClearZ
fonte
4

Se o que você quer dizer com "o que realmente produz quebra", é "como funciona" - consulte o blog de Raymond Chen para obter detalhes https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

Os iteradores C # geram código muito complicado.

Tony Lee
fonte
12
Bem, eles só são complicados se você se importa com o código em que são compilados. O código que os utiliza é bastante simples.
Robert Rossney 24/10/08
@ Robert, foi isso que eu quis dizer, por isso atualizei a resposta para incluir o seu comentário
Tony Lee
-2

A palavra-chave yield é usada junto com a palavra-chave return para fornecer um valor ao objeto enumerador. retorno de rendimento especifica o valor ou valores retornados. Quando a declaração de retorno de rendimento é alcançada, o local atual é armazenado. A execução é reiniciada nesse local na próxima vez que o iterador for chamado.

Para explicar o significado usando um exemplo:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

Os valores retornados quando iterados são: 0, 2, 5.

É importante observar que a variável de contador neste exemplo é uma variável local. Após a segunda iteração que retorna o valor 2, a terceira iteração começa de onde saiu antes, preservando o valor anterior da variável local denominada counter, que era 2.

Eranga Dissanayaka
fonte
11
Você não explicou o que yield breakfaz
Jay Sullivan
Eu não acho que yield returnrealmente suporta o retorno de vários valores. Talvez não seja isso que você realmente quis dizer, mas é assim que eu li.
Sam
Sam - o método SampleNumbers com várias instruções de retorno de rendimento realmente funciona, o valor do iterador é retornado imediatamente e a execução é retomada quando o próximo valor for solicitado. Vi pessoas terminando um método como esse com "quebra de rendimento", mas é desnecessário. Bater a fim do método também termina a iteração
Loren Paulsen
a razão pela qual este é um exemplo ruim yield breaké que ele não contém um enumerador no nível do idioma como a foreach- ao usar um enumerador, o yield breakvalor real é fornecido. este exemplo parece um loop desenrolado. você quase nunca verá esse código no mundo real (todos nós podemos pensar em alguns casos extremos, com certeza) também, não há "iterador" aqui. o "bloco iterador" não pode se estender além do método como uma questão de especificação de linguagem. o que realmente está sendo retornado é um "enumerável", consulte também: stackoverflow.com/questions/742497/…
Shaun Wilson