आप उपयोग कर रहे हैं 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 के साथ विघटित हो गई है।
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()
{
}
}
यह अमान्य सी # कोड है, क्योंकि कंपाइलर को उन चीजों को करने की अनुमति है जो भाषा अनुमति नहीं देती है, लेकिन जो आईएल में कानूनी हैं - उदाहरण के लिए चर का नामकरण एक तरह से आप नाम टकराव से बचने के लिए नहीं कर सकते।
लेकिन जैसा कि आप देख सकते हैं, AllIndexesOf
एकमात्र निर्माण करता है और एक वस्तु लौटाता है, जिसका निर्माता केवल कुछ राज्य को इनिशियलाइज़ करता है। GetEnumerator
केवल वस्तु की प्रतिलिपि बनाता है। असली काम तब किया जाता है जब आप गणना ( MoveNext
विधि को बुलाकर ) शुरू करते हैं।