Come yield
implementa lo schema di lazy loading
?In che modo la resa implementa il modello di caricamento lento?
risposta
l'implementazione del rendimento non raggiunge il codice finché non è necessario.
Ad esempio, questo codice:
public IEnumerable<int> GetInts()
{
yield return 1;
yield return 2;
yield return 3;
}
effettivamente compilare in una classe nidificata che implementa IEnumerable<int>
e il corpo di GetInts()
restituirà un'istanza di tale classe.
Utilizzando riflettore è possibile vedere:
public IEnumerable<int> GetInts()
{
<GetInts>d__6d d__d = new <GetInts>d__6d(-2);
d__d.<>4__this = this;
return d__d;
}
Modifica - l'aggiunta più informazioni GetInts
implementazione:
Il modo in cui questa implementazione rende pigri si basa sul metodo di Enumerator
MoveNext()
. Quando la classe nidificata enumerabile viene generata (<GetInts>d__6d
nell'esempio), ha uno stato e ad ogni stato è collegato un valore (questo è un caso semplice, nei casi più avanzati il valore verrà valutato quando il codice raggiunge lo stato). Se diamo uno sguardo nel codice MoveNext()
di <GetInts>d__6d
vedremo lo stato:
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
this.<>2__current = 2;
this.<>1__state = 2;
return true;
case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;
case 3:
this.<>1__state = -1;
break;
}
return false;
}
Quando l'enumeratore viene chiesto per l'oggetto corrente che restituisce l'oggetto, che è collegato allo stato attuale.
Al fine di dimostrare che il codice viene valutato solo in caso di necessità si può guardare a questo esempio:
[TestFixture]
public class YieldExample
{
private int flag = 0;
public IEnumerable<int> GetInts()
{
yield return 1;
flag = 1;
yield return 2;
flag = 2;
yield return 3;
flag = 3;
}
[Test]
public void Test()
{
int expectedFlag = 0;
foreach (var i in GetInts())
{
Assert.That(flag, Is.EqualTo(expectedFlag));
expectedFlag++;
}
Assert.That(flag, Is.EqualTo(expectedFlag));
}
}
Spero che sia un po 'più chiaro. Consiglio di dare un'occhiata al codice con Reflector e osservare il codice compilato mentre modifichi il codice "yield".
Se vuoi sapere di più su ciò che il compilatore sta facendo quando si utilizza yield return
, controllare questo articolo di Jon Skeet: Iterator block implementation details
Reindirizzamento Briljant, in realtà. +1 – peSHIr
Fondamentalmente iteratori implementated utilizzando yield
dichiarazioni vengono compilate in una classe che implementa un state machine.
Se non si utilizza mai foreach
(= itera su e effettivamente utilizzato) il codice restituito IEnumerable<T>
, il codice non viene mai effettivamente eseguito. E se lo fai, viene eseguito solo il codice minimo necessario per determinare il valore successivo da restituire, solo per riprendere l'esecuzione quando viene richiesto un valore successivo.
Si può effettivamente vedere questo comportamento accadere quando si esegue un passo singolo tale codice nel debugger. Provalo almeno una volta: penso che sia illuminante vederlo accadere passo dopo passo.
@Elisha: fornire ulteriori dettagli su GetInts(). –
@masoud ramezani, ha aggiunto ulteriori informazioni sulla classe di enumerazione nidificata di GetInts. – Elisha
Grazie per la risposta completa. –