Sie verwenden yield return
. Dabei schreibt der Compiler Ihre Methode in eine Funktion um, die eine generierte Klasse zurückgibt, die eine Zustandsmaschine implementiert.
Im Allgemeinen werden die Einheimischen in Felder dieser Klasse umgeschrieben, und jeder Teil Ihres Algorithmus zwischen den yield return
Anweisungen wird zu einem Status. Sie können mit einem Dekompiler überprüfen, wie diese Methode nach der Kompilierung aussehen wird (stellen Sie sicher, dass die intelligente Dekompilierung deaktiviert ist, die dazu führen würdeyield return
).
Das Fazit lautet jedoch: Der Code Ihrer Methode wird erst ausgeführt, wenn Sie mit der Iteration beginnen.
Die übliche Methode, um nach Voraussetzungen zu suchen, besteht darin, Ihre Methode in zwei Teile zu teilen:
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;
}
}
Dies funktioniert, weil sich die erste Methode wie erwartet verhält (sofortige Ausführung) und die von der zweiten Methode implementierte Zustandsmaschine zurückgibt.
Beachten Sie, dass Sie auch die überprüfen sollten , str
Parameter für null
, da Erweiterungen Methoden können aufgefordert werden , null
Werte, da sie nur syntaktischer Zucker sind.
Wenn Sie neugierig sind, was der Compiler mit Ihrem Code macht, finden Sie hier Ihre Methode, die mit dotPeek mithilfe der Option Vom Compiler generierten Code anzeigen dekompiliert wurde .
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()
{
}
}
Dies ist ein ungültiger C # -Code, da der Compiler Dinge tun darf, die die Sprache nicht zulässt, die aber in IL legal sind - zum Beispiel die Variablen so zu benennen, dass Namenskollisionen nicht vermieden werden können.
Wie Sie jedoch sehen können, erstellt AllIndexesOf
und gibt der einzige ein Objekt zurück, dessen Konstruktor nur einen bestimmten Status initialisiert. GetEnumerator
kopiert nur das Objekt. Die eigentliche Arbeit ist erledigt, wenn Sie mit der Aufzählung beginnen (durch Aufrufen der MoveNext
Methode).