आपको C# में yield की आवश्यकता क्यों है? आलसी संग्रहों का रहस्य
\n\nकल्पना करें: आप एक ऐसा मेथड लिख रहे हैं जिसे 1 से दस लाख तक की संख्याओं की सूची लौटानी है। सबसे पहले जो विचार आता है — वह है List<int> बनाना, उसे भरना और लौटाना। लेकिन क्या होगा यदि कॉल करने वाला कोड केवल पहले 10 तत्व चाहता है? आपने फिर भी दस लाख ऑब्जेक्ट बनाने में समय बर्बाद किया, मेमोरी घेरी — और सब व्यर्थ।
यहाँ पर दृश्य में आता है C# में yield कीवर्ड। यह केवल सिंटैक्टिक शुगर नहीं है, बल्कि आलसी (lazy) अनुक्रम बनाने के लिए एक शक्तिशाली उपकरण है। इस लेख में हम वास्तविक उदाहरणों के साथ yield return और yield break को समझेंगे, जानेंगे कि वे अंदरूनी रूप से कैसे काम करते हैं, और दैनिक विकास में उन्हें लागू करना सीखेंगे।
\n\nयह ट्यूटोरियल Stack Overflow के एक वास्तविक प्रश्न पर आधारित है, जिसे हज़ारों बार देखा गया। हमने इसे शैक्षिक सामग्री के लिए अनुकूलित किया है, जिसमें C# कोड के चरण-दर-चरण उदाहरण और उपयोगी व्यावहारिक सुझाव जोड़े गए हैं।
\n\nC# में yield क्या है? सरल शब्दों में
\n\nyield C# में एक संदर्भ-संवेदी कीवर्ड है जो कंपाइलर को बताता है: "यह मेथड एक इटरेटर है"। जब आप yield return लिखते हैं, तो मेथड सब कुछ एक साथ निष्पादित नहीं करता। इसके बजाय, यह तत्वों को एक-एक करके लौटाता है, जब कॉल करने वाला कोड उनका अनुरोध करता है। इसे आलसी निष्पादन कहा जाता है।
सामान्य return के विपरीत, जो मेथड को समाप्त करता है, yield return मेथड को रोकता है, उसकी स्थिति को याद रखता है और वर्तमान तत्व लौटाता है। अगली कॉल पर, मेथड उसी स्थान से काम जारी रखता है जहाँ वह रुका था।
सिंटैक्स: yield return और yield break
\n\nC# में yield के दो रूप हैं:
\n\n- \n
- yield return — अनुक्रम का अगला तत्व लौटाता है। \n
- yield break — तुरंत पुनरावृत्ति समाप्त करता है (जैसे लूप में
break)। \n
yield का उपयोग करने वाले मेथड को इनमें से एक प्रकार लौटाना चाहिए:
\n- \n
IEnumerable<T>\n IEnumerable\n IAsyncEnumerable<T>(C# 8.0 से शुरू) \n IEnumerator<T>याIEnumerator\n
पहला उदाहरण: सरल से जटिल तक
\n\nआइए एक सबसे सरल इटरेटर लिखें जो 1 से 5 तक संख्याएँ उत्पन्न करता है:
\n\npublic static IEnumerable<int> GetNumbers()\n{\n yield return 1;\n yield return 2;\n yield return 3;\n yield return 4;\n yield return 5;\n}\n\n// उपयोग:\nforeach (int number in GetNumbers())\n{\n Console.WriteLine(number);\n}\n\nआउटपुट:
\n1\n2\n3\n4\n5\n\nपहली नज़र में, यह एक सामान्य सूची जैसा दिखता है। लेकिन अंतर बहुत बड़ा है: प्रत्येक तत्व केवल तब उत्पन्न होता है जब foreach उसका अनुरोध करता है। पूरी सूची के लिए कोई मेमोरी आवंटन नहीं!
यह अंदरूनी रूप से कैसे काम करता है?
\n\nC# कंपाइलर yield वाले मेथड को एक पूर्ण स्टेट मशीन (state machine) में बदल देता है। यह एक ऐसा क्लास बनाता है जो IEnumerable<T> और IEnumerator<T> को लागू करता है। यह क्लास संग्रहीत करता है:
- \n
- वर्तमान स्थिति (हम मेथड की किस पंक्ति पर रुके थे)। \n
- मेथड के स्थानीय चर। \n
- वर्तमान ऑब्जेक्ट का संदर्भ (
this), यदि मेथड स्थैतिक नहीं है। \n
हर बार जब MoveNext() कॉल किया जाता है (और foreach यही करता है), स्टेट मशीन अंतिम रुकने वाली जगह से निष्पादन फिर से शुरू करती है।
वास्तविक उदाहरण: बड़ी फ़ाइल पढ़ना
\n\nकल्पना करें कि आपको 10 GB के लॉग फ़ाइल को प्रोसेस करना है। यदि आप इसे पूरी तरह से मेमोरी में पढ़ते हैं, तो प्रोग्राम OutOfMemoryException के साथ क्रैश हो जाएगा। yield के साथ, आप फ़ाइल को पंक्ति दर पंक्ति पढ़ सकते हैं और प्रत्येक पंक्ति को चलते-फिरते प्रोसेस कर सकते हैं:
public static IEnumerable<s