Si utilizza yield return
. In tal caso, il compilatore riscriverà il metodo in una funzione che restituisce una classe generata che implementa una macchina a stati.
In linea generale, riscrive i locali nei campi di quella classe e ogni parte dell'algoritmo tra le istruzioni yield return
diventa uno stato. È possibile verificare con un decompilatore che cosa questo metodo diventa dopo la compilazione (assicurarsi di disattivare la decompilazione intelligente che produrrebbe yield return
).
Ma la riga di fondo è: il codice del metodo non verrà eseguito finché non si avvia l'iterazione.
Il solito modo per verificare la presenza di presupposti è quello di dividere il metodo in due:
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
if (str == null)
throw new ArgumentNullException("str");
if (searchText == null)
throw new ArgumentNullException("searchText");
return AllIndexesOfCore(str, searchText);
}
private static IEnumerable<int> AllIndexesOfCore(string str, string searchText)
{
for (int index = 0; ; index += searchText.Length)
{
index = str.IndexOf(searchText, index);
if (index == -1)
break;
yield return index;
}
}
Questo funziona perché il primo metodo si comporterà esattamente come ci si aspetta (esecuzione immediata), e restituirà la macchina dello Stato implementato dal secondo metodo.
noti che si dovrebbe anche controllare il parametro str
per null
, perché i metodi estensioni possono essere chiamati null
valori, sono solo zucchero sintattico.
Se siete curiosi di sapere che cosa il compilatore fa al vostro codice, qui è il tuo metodo, decompilato con dotPeek utilizzando il Mostra generato dal compilatore codice opzione.
public static IEnumerable<int> AllIndexesOf(this string str, string searchText)
{
Test.<AllIndexesOf>d__0 allIndexesOfD0 = new Test.<AllIndexesOf>d__0(-2);
allIndexesOfD0.<>3__str = str;
allIndexesOfD0.<>3__searchText = searchText;
return (IEnumerable<int>) allIndexesOfD0;
}
[CompilerGenerated]
private sealed class <AllIndexesOf>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
private int <>2__current;
private int <>1__state;
private int <>l__initialThreadId;
public string str;
public string <>3__str;
public string searchText;
public string <>3__searchText;
public int <index>5__1;
int IEnumerator<int>.Current
{
[DebuggerHidden] get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden] get
{
return (object) this.<>2__current;
}
}
[DebuggerHidden]
public <AllIndexesOf>d__0(int <>1__state)
{
base..ctor();
this.<>1__state = param0;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
Test.<AllIndexesOf>d__0 allIndexesOfD0;
if (Environment.CurrentManagedThreadId == this.<>l__initialThreadId && this.<>1__state == -2)
{
this.<>1__state = 0;
allIndexesOfD0 = this;
}
else
allIndexesOfD0 = new Test.<AllIndexesOf>d__0(0);
allIndexesOfD0.str = this.<>3__str;
allIndexesOfD0.searchText = this.<>3__searchText;
return (IEnumerator<int>) allIndexesOfD0;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator) this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
}
bool IEnumerator.MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
if (this.searchText == null)
throw new ArgumentNullException("searchText");
this.<index>5__1 = 0;
break;
case 1:
this.<>1__state = -1;
this.<index>5__1 += this.searchText.Length;
break;
default:
return false;
}
this.<index>5__1 = this.str.IndexOf(this.searchText, this.<index>5__1);
if (this.<index>5__1 != -1)
{
this.<>2__current = this.<index>5__1;
this.<>1__state = 1;
return true;
}
goto default;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
}
Questo è valido codice C#, perché il compilatore è permesso di fare le cose la lingua non consente, ma che sono legali in IL - per esempio la denominazione delle variabili in un modo che non si poteva evitare nome collisioni.
Tuttavia, come potete vedere, lo AllIndexesOf
solo costruisce e restituisce un oggetto, il cui costruttore inizializza solo uno stato. GetEnumerator
copia solo l'oggetto. Il vero lavoro viene eseguito quando si avvia l'enumerazione (chiamando il metodo MoveNext
).
Hai provato passando attraverso il codice? Probabilmente questo verrà risolto abbastanza rapidamente. –
Cosa * succede * succede? (Esegue * un'eccezione *, in tal caso, quale riga e quale?) – user2864740
@ user2864740 Ho descritto tutto ciò che accade. Nessuna eccezione, solo un test fallito e un metodo di esecuzione. – ArtOfCode