yield
कीवर्ड आप एक बनाने की अनुमति देता IEnumerable<T>
एक पर फार्म में iterator ब्लॉक । यह इटरेटर ब्लॉक आवर्ती निष्पादन का समर्थन करता है और यदि आप अवधारणा से परिचित नहीं हैं तो यह लगभग जादुई दिखाई दे सकता है। हालांकि, दिन के अंत में यह केवल कोड है जो बिना किसी अजीब चाल के निष्पादित होता है।
एक इट्रिक्टर ब्लॉक को सिंटैक्टिक शुगर के रूप में वर्णित किया जा सकता है जहां कंपाइलर एक राज्य मशीन बनाता है जो ट्रैक करता है कि एन्यूमरेबल की गणना कितनी दूर तक आगे बढ़ी है। किसी एन्यूमरेट को एन्यूमरेट करने के लिए, आप अक्सर एक foreach
लूप का उपयोग करते हैं । हालांकि, एक foreach
लूप भी सिंथेटिक चीनी है। तो आप वास्तविक कोड से हटाए गए दो अमूर्त हैं, यही वजह है कि शुरू में यह समझना मुश्किल हो सकता है कि यह सब एक साथ कैसे काम करता है।
मान लें कि आपके पास एक बहुत ही सरल पुनरावृत्ति अवरोधक है:
IEnumerable<int> IteratorBlock()
{
Console.WriteLine("Begin");
yield return 1;
Console.WriteLine("After 1");
yield return 2;
Console.WriteLine("After 2");
yield return 42;
Console.WriteLine("End");
}
रियल इटरेटर ब्लॉक में अक्सर स्थितियां और लूप होते हैं, लेकिन जब आप शर्तों की जांच करते हैं और लूप को अनियंत्रित करते हैं तो वे अभी भी समाप्त हो जाते हैं क्योंकि yield
बयान अन्य कोड के साथ interleaved हैं।
पुनरावृत्ति करने के लिए पुनरावृत्ति अवरोधक a foreach
लूप का उपयोग किया जाता है:
foreach (var i in IteratorBlock())
Console.WriteLine(i);
यहाँ आउटपुट है (यहाँ कोई आश्चर्य नहीं):
शुरू
1
1 के बाद
2
2 के बाद
42
समाप्त
जैसा कि ऊपर कहा गया foreach
है, शक्करयुक्त चीनी:
IEnumerator<int> enumerator = null;
try
{
enumerator = IteratorBlock().GetEnumerator();
while (enumerator.MoveNext())
{
var i = enumerator.Current;
Console.WriteLine(i);
}
}
finally
{
enumerator?.Dispose();
}
इसे हटाने के प्रयास में मैंने एक अनुक्रम आरेख को हटा दिया है जिसमें हटाए गए सार हैं:
संकलक द्वारा बनाई गई राज्य मशीन भी एन्यूमरेटर को लागू करती है लेकिन आरेख को अधिक स्पष्ट करने के लिए मैंने उन्हें अलग-अलग उदाहरणों के रूप में दिखाया है। (जब राज्य मशीन दूसरे धागे से एनुमरेट की जाती है तो आपको वास्तव में अलग-अलग उदाहरण मिलते हैं, लेकिन यह विवरण यहां महत्वपूर्ण नहीं है)
हर बार जब आप अपने itter ब्लॉक को कॉल करते हैं तो स्टेट मशीन का एक नया उदाहरण बनता है। हालाँकि, इटरेटर ब्लॉक में आपका कोई भी कोड enumerator.MoveNext()
पहली बार निष्पादित होने तक निष्पादित नहीं होता है। इस प्रकार कार्य को स्थगित कर दिया जाता है। यहाँ एक (बल्कि मूर्खतापूर्ण) उदाहरण है:
var evenNumbers = IteratorBlock().Where(i => i%2 == 0);
इस बिंदु पर पुनरावृत्ति निष्पादित नहीं किया गया है। Where
खंड एक नया बनाता है IEnumerable<T>
कि wraps IEnumerable<T>
द्वारा दिया IteratorBlock
लेकिन इस गणनीय अभी तक प्रगणित किया जाना है। यह तब होता है जब आप एक foreach
लूप निष्पादित करते हैं :
foreach (var evenNumber in evenNumbers)
Console.WriteLine(eventNumber);
यदि आप गणना करने योग्य दो बार गणना करते हैं तो हर बार राज्य मशीन का एक नया उदाहरण बनाया जाता है और आपका पुनरावृत्ति खंड एक ही कोड को दो बार निष्पादित करेगा।
सूचना है कि LINQ तरीकों की तरह ToList()
, ToArray()
, First()
, Count()
आदि एक का उपयोग करेगा foreach
पाश गणनीय की गणना करने में। उदाहरण के लिए ToList()
, गणना करने वाले सभी तत्वों की गणना की जाएगी और उन्हें एक सूची में संग्रहीत किया जाएगा। अब आप पुनरावृत्ति ब्लॉक किए हुए पुनरावृत्ति ब्लॉकर के सभी तत्वों को पाने के लिए सूची तक पहुँच सकते हैं। सीपीयू का उपयोग करने के बीच एक व्यापार बंद है, जिसमें कई बार उपयोग करने के लिए कई बार उपयोग करने के लिए एन्यूमरेशन के तत्वों को स्टोर करने के लिए एन्यूमरबल के तत्वों का उत्पादन किया जाता है ToList()
।