Anda sedang menggunakan yield return
. Saat melakukannya, kompilator akan menulis ulang metode Anda menjadi fungsi yang mengembalikan kelas yang dihasilkan yang mengimplementasikan mesin status.
Secara umum, ini menulis ulang penduduk setempat ke bidang kelas itu dan setiap bagian dari algoritme Anda di antara yield return
instruksi menjadi status. Anda dapat memeriksa dengan decompiler akan menjadi apa metode ini setelah kompilasi (pastikan untuk mematikan dekompilasi cerdas yang akan menghasilkan yield return
).
Tetapi intinya adalah: kode metode Anda tidak akan dieksekusi sampai Anda mulai mengulang.
Cara biasa untuk memeriksa prasyarat adalah dengan membagi metode Anda menjadi dua:
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;
}
}
Ini berfungsi karena metode pertama akan berperilaku seperti yang Anda harapkan (eksekusi langsung), dan akan mengembalikan mesin status yang diimplementasikan oleh metode kedua.
Perhatikan bahwa Anda juga harus memeriksa str
parameter untuk null
, karena metode ekstensi dapat dipanggil pada null
nilai, karena mereka hanya gula sintaksis.
Jika Anda penasaran tentang apa yang dilakukan compiler pada kode Anda, berikut adalah metode Anda, didekompilasi dengan dotPeek menggunakan opsi Show Compiler-generated Code .
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()
{
}
}
Ini adalah kode C # yang tidak valid, karena kompilator diizinkan untuk melakukan hal-hal yang tidak diizinkan oleh bahasa, tetapi legal di IL - misalnya menamai variabel dengan cara yang tidak dapat Anda lakukan untuk menghindari benturan nama.
Tapi seperti yang Anda lihat, AllIndexesOf
satu - satunya membangun dan mengembalikan sebuah objek, yang konstruktornya hanya menginisialisasi beberapa keadaan. GetEnumerator
hanya menyalin objek. Pekerjaan sebenarnya dilakukan ketika Anda mulai menghitung (dengan memanggil MoveNext
metode).