คุณกำลังใช้yield return
. เมื่อทำเช่นนั้นคอมไพเลอร์จะเขียนเมธอดของคุณใหม่เป็นฟังก์ชันที่ส่งคืนคลาสที่สร้างขึ้นซึ่งใช้เครื่องสถานะ
พูดอย่างกว้าง ๆ มันเขียนภาษาท้องถิ่นไปยังฟิลด์ของคลาสนั้นและแต่ละส่วนของอัลกอริทึมของคุณระหว่างyield return
คำสั่งจะกลายเป็นสถานะ คุณสามารถตรวจสอบกับผู้ถอดรหัสว่าวิธีนี้กลายเป็นอย่างไรหลังจากการคอมไพล์ (ตรวจสอบให้แน่ใจว่าได้ปิดการแยกส่วนอัจฉริยะซึ่งจะก่อให้เกิดyield return
)
แต่สิ่งที่สำคัญที่สุดคือโค้ดของวิธีการของคุณจะไม่ถูกเรียกใช้จนกว่าคุณจะเริ่มทำซ้ำ
วิธีปกติในการตรวจสอบเงื่อนไขเบื้องต้นคือการแบ่งวิธีของคุณออกเป็นสองวิธี:
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;
}
}
วิธีนี้ใช้ได้ผลเนื่องจากวิธีแรกจะทำงานเหมือนกับที่คุณคาดไว้ (การดำเนินการทันที) และจะส่งคืนเครื่องสถานะที่ใช้งานโดยวิธีที่สอง
โปรดทราบว่าคุณควรตรวจสอบstr
พารามิเตอร์null
ด้วยเนื่องจากเมธอดส่วนขยายสามารถเรียกใช้null
ค่าได้เนื่องจากเป็นเพียงแค่น้ำตาลที่เป็นประโยค
หากคุณสงสัยเกี่ยวกับสิ่งที่คอมไพเลอร์ทำกับโค้ดของคุณนี่คือวิธีการของคุณโดยแยกคอมไพล์ด้วย dotPeek โดยใช้ตัวเลือกShow Compiler- created 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()
{
}
}
รหัส C # ไม่ถูกต้องเนื่องจากคอมไพเลอร์ได้รับอนุญาตให้ทำในสิ่งที่ภาษาไม่อนุญาต แต่ถูกกฎหมายใน IL เช่นการตั้งชื่อตัวแปรในลักษณะที่คุณไม่สามารถหลีกเลี่ยงการชนกันของชื่อได้
แต่อย่างที่คุณเห็นสิ่งAllIndexesOf
เดียวที่สร้างและส่งคืนอ็อบเจ็กต์ซึ่งตัวสร้างจะเริ่มต้นบางสถานะเท่านั้น GetEnumerator
คัดลอกวัตถุเท่านั้น งานจริงจะเสร็จสิ้นเมื่อคุณเริ่มแจกแจง (โดยเรียกใช้MoveNext
เมธอด)