Cosa significa "cedimento del rendimento"; fare in C #?


494

Ho visto questa sintassi in MSDN:, yield breakma non so cosa faccia. Qualcuno sa?


26
Tuttavia, la documentazione MSDN lo menziona ma non lo spiega. Questo è un brutto modo di prendere in giro uno sviluppatore affamato di conoscenza.
Phil

3
Il rendimento del rendimento elimina la necessità di un backing list, ovvero non è necessario codificare qualcosa come MyList.Add(...)fare yield return .... Se hai bisogno di uscire prematuramente dal loop e restituire la backing list virtuale che usiyield break;
The Muffin Man,

Risposte:


529

Specifica che un iteratore è giunto al termine. Puoi pensare yield breaka returnun'istruzione che non restituisce un valore.

Ad esempio, se si definisce una funzione come iteratore, il corpo della funzione potrebbe apparire così:

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

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

Nota che dopo che il ciclo ha completato tutti i suoi cicli, l'ultima riga viene eseguita e vedrai il messaggio nell'app della console.

O così con 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");

In questo caso l'ultima istruzione non viene mai eseguita perché abbiamo lasciato la funzione in anticipo.


8
Potrebbe essere semplicemente breakinvece che yield breaknel tuo esempio sopra? Il compilatore non si lamenta di questo.
orad,

85
@orad un semplice breakin questo caso fermerebbe il ciclo, ma non interromperebbe l'esecuzione del metodo, quindi l'ultima riga verrebbe eseguita e il "Won't see me text" verrebbe effettivamente visto.
Damir Zekić,

5
@Damir Zekić Potresti anche aggiungere alla tua risposta perché dovresti preferire la rottura del rendimento rispetto al rendimento e quali sono le differenze tra i due?
Bruno Costa,

11
@ DamirZekić restituisce null e la rottura del rendimento non è la stessa. Se si restituisce null, è possibile NullReferenceExceptionottenere l'enumeratore di IEnumerable, mentre con l'interruzione del rendimento non si ottiene (esiste un'istanza senza elementi).
Bruno Costa,

6
@BrunoCosta Il tentativo di restituire regolarmente lo stesso metodo genera un errore del compilatore. Utilizzando gli yield return xavvisi del compilatore, si desidera che questo metodo sia uno zucchero sintattico per la creazione di un Enumeratoroggetto. Questo enumeratore ha metodo MoveNext()e proprietà Current. MoveNext () esegue il metodo fino a yield returnun'istruzione e trasforma quel valore in Current. Al successivo richiamo di MoveNext, l'esecuzione continua da lì. yield breakimposta Current su null, segnalando la fine di questo enumeratore, in modo che un foreach (var x in myEnum())end finisca.
Wolfzoon,

57

Termina un blocco iteratore (ad esempio dice che non ci sono più elementi in IEnumerable).


2
con [+1] - sebbene accademicamente non ci siano iteratori in .NET. solo enumeratori (una direzione, avanti.)
Shaun Wilson,

@Shaun Wilson, ma con la yieldparola chiave puoi iterare la raccolta in entrambe le direzioni, inoltre puoi prendere ogni elemento successivo della raccolta non in fila
monstr

@monstr you puoi iterare la collezione in qualsiasi modo e non yielddevi farlo. non sto dicendo che ti sbagli, capisco cosa stai suggerendo; ma accademicamente non ci sono iteratori in .NET, solo enumeratori (una direzione, avanti) - a differenza di stdc ++ non esiste un "framework iteratore generico" definito nel CTS / CLR. LINQ aiuta a colmare il divario con i metodi di estensione che utilizzano yield return e anche i metodi di callback , ma sono metodi di estensione di prima classe, non iteratori di prima classe. il risultante IEnumerablenon può di per sé iterare in alcuna direzione diversa dall'inoltro del chiamante.
Shaun Wilson,

29

Indica all'iteratore che ha raggiunto la fine.

Come esempio:

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;
    }
}

21

yieldfondamentalmente fa in modo che un IEnumerable<T>metodo si comporti in modo simile a un thread pianificato in modo cooperativo (anziché preventivamente).

yield returnè come un thread che chiama una funzione "programma" o "sospensione" per rinunciare al controllo della CPU. Proprio come un thread, il IEnumerable<T>metodo recupera i controlli nel punto immediatamente successivo, con tutte le variabili locali che hanno gli stessi valori che avevano prima che il controllo venisse abbandonato.

yield break è come un thread che raggiunge la fine della sua funzione e termina.

Le persone parlano di una "macchina a stati", ma una macchina a stati è tutto un "thread". Un thread ha un certo stato (vale a dire valori di variabili locali) e ogni volta che è pianificato prende alcune azioni per raggiungere un nuovo stato. Il punto chiave yieldè che, a differenza dei thread del sistema operativo a cui siamo abituati, il codice che lo utilizza viene congelato nel tempo fino a quando l'iterazione non viene avanzata o terminata manualmente.



9

L' yield breakistruzione provoca l'arresto dell'enumerazione. In effetti, yield breakcompleta l'enumerazione senza restituire altri elementi.

Considera che in realtà ci sono due modi in cui un metodo iteratore potrebbe smettere di iterare. In un caso, la logica del metodo potrebbe naturalmente uscire dal metodo dopo aver restituito tutti gli elementi. Ecco un esempio:

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.");
}

Nell'esempio sopra, il metodo iteratore interromperà naturalmente l'esecuzione una volta maxCounttrovati i numeri primi.

La yield breakdichiarazione è un altro modo per l'iteratore di smettere di enumerare. È un modo per uscire presto dall'enumerazione. Ecco lo stesso metodo di cui sopra. Questa volta, il metodo ha un limite alla quantità di tempo che il metodo può eseguire.

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.");
}

Si noti la chiamata a yield break. In effetti, sta uscendo presto dall'enumerazione.

Notare anche che yield breakfunziona diversamente da una semplice pianura break. Nell'esempio sopra, yield breakesce dal metodo senza effettuare la chiamata a Debug.WriteLine(..).


7

Qui http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ è un ottimo esempio:

pubblico IEnumerable <int> Range (int min, int max)
{
   while (true)
   {
      if (min> = max)
      {
         pausa di rendimento;
      }
      rendimento minimo min ++;
   }
}

e spiegazione, che se yield breakun'istruzione viene colpita all'interno di un metodo, l'esecuzione di quel metodo si interrompe senza ritorno. Ci sono alcune situazioni temporali, in cui non si desidera dare alcun risultato, quindi è possibile utilizzare l'intervallo di rendimento.


5

la rottura del rendimento è solo un modo per dire ritorno per l'ultima volta e non restituire alcun valore

per esempio

// 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;
 }

4

Se ciò che intendi con "cosa fa realmente la rottura del rendimento", è "come funziona" - Vedi il blog di Raymond Chen per i dettagli https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

Gli iteratori C # generano un codice molto complicato.


12
Bene, sono complicati solo se ti interessa il codice in cui sono compilati. Il codice che li utilizza è piuttosto semplice.
Robert Rossney,

@Robert, questo è ciò che intendevo, quindi ho aggiornato la risposta per includere il tuo commento
Tony Lee,

-2

La parola chiave yield viene utilizzata insieme alla parola chiave return per fornire un valore all'oggetto enumeratore. il rendimento restituisce il valore oi valori restituiti. Quando viene raggiunta l'istruzione return return, viene memorizzata la posizione corrente. L'esecuzione viene riavviata da questa posizione al successivo richiamo dell'iteratore.

Per spiegare il significato usando un esempio:

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

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

I valori restituiti quando viene ripetuto sono: 0, 2, 5.

È importante notare che la variabile contatore in questo esempio è una variabile locale. Dopo la seconda iterazione che restituisce il valore di 2, la terza iterazione inizia da dove era stata lasciata prima, preservando il valore precedente della variabile locale denominata counter che era 2.


11
Non hai spiegato cosa yield breakfa
Jay Sullivan,

Non credo in yield returnrealtà supporti la restituzione di più valori. Forse non è quello che volevi dire, ma è così che l'ho letto.
Sam,

Sam: il metodo SampleNumbers con più dichiarazioni di ritorno di rendimento funziona in effetti, il valore dell'iteratore viene restituito immediatamente e l'esecuzione viene ripresa quando viene richiesto il valore successivo. Ho visto le persone terminare un metodo come questo con "cedimento del rendimento", ma non è necessario. Colpire la fine del metodo termina anche l'iteratore
Loren Paulsen,

il motivo per cui questo è un cattivo esempio yield breakè che non contiene un enumeratore a livello di lingua come un foreach- quando si utilizza un enumeratore yield breakfornisce un valore reale. questo esempio sembra un ciclo non srotolato. non vedrai quasi mai questo codice nel mondo reale (possiamo tutti pensare ad alcuni casi limite, certo), anche qui non c'è un "iteratore". il "blocco iteratore" non può estendersi oltre il metodo come una questione di specifica del linguaggio. ciò che viene effettivamente restituito è un "enumerabile", vedi anche: stackoverflow.com/questions/742497/…
Shaun Wilson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.