Sie yield return
verwenden. Wenn Sie dies tun, überschreibt der Compiler Ihre Methode in eine Funktion, die eine generierte Klasse zurückgibt, die eine Zustandsmaschine implementiert.
Allgemein gesagt schreibt es Ortsansätze zu Feldern dieser Klasse um und jeder Teil Ihres Algorithmus zwischen den yield return
Anweisungen wird zu einem Zustand. Sie können mit einem Decompiler überprüfen, was diese Methode nach der Kompilierung wird (stellen Sie sicher, dass Sie die intelligente Dekompilierung deaktivieren, die yield return
erzeugt).
Aber die Quintessenz ist: der Code Ihrer Methode wird nicht ausgeführt, bis Sie Iteration beginnen.
Der üblicher Weg für Voraussetzungen zu überprüfen, ist Ihre Methode in zwei aufzuspalten:
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;
}
}
Das funktioniert, weil die erste Methode wie Sie erwarten (sofortige Ausführung) verhalten wird, und die Zustandsmaschine zurückkehrt implementiert mit der zweiten Methode.
Beachten Sie, dass Sie auch die str
Parameter für null
überprüfen sollten, da Erweiterungen Methoden können auf null
Werte genannt werden, da sie nur syntaktischer Zucker sind.
Wenn Sie neugierig sind, was der Compiler den Code der Fall ist, hier ist Ihre Methode, dekompilierten mit dotPeek dem anzeigen Compiler generierten Code Option.
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 ungültig C# -Code, weil die Compiler Dinge zu tun erlaubt ist, die Sprache nicht erlaubt, die aber in IL legal ist - zum Beispiel der Variablen in einer Art und Weise zu benennen könnten Sie nicht Namen zu vermeiden Kollisionen.
Aber wie Sie sehen können, das AllIndexesOf
Konstrukte und gibt nur ein Objekt zurück, dessen Konstruktor nur einige Zustände initialisiert. GetEnumerator
kopiert nur das Objekt. Die eigentliche Arbeit ist erledigt, wenn Sie mit dem Aufzählen beginnen (indem Sie die Methode MoveNext
aufrufen).
Haben Sie versucht, durch den Code treten? Das wird es wahrscheinlich ziemlich schnell lösen. –
Was * passiert *? (Es wirft * eine * Ausnahme; wenn ja, welche und welche Zeile?) – user2864740
@ user2864740 Ich habe alles beschrieben, was passiert. Keine Ausnahmen, nur ein fehlgeschlagener Test und eine Laufmethode. – ArtOfCode