C# foreach में वेरिएबल का पुन: उपयोग क्यों करता है? शुरुआती लोगों के लिए विश्लेषण

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: एक पहेली जो शुरुआती लोगों को परेशान करती है

\n\n

एक स्थिति की कल्पना करें: आप C# में एक सरल foreach लूप लिख रहे हैं, लूप के अंदर एक lambda एक्सप्रेशन सहेज रहे हैं या एक async टास्क चला रहे हैं, और अचानक आपको पूरी तरह से अप्रत्याशित परिणाम मिलता है। वे सभी वेरिएबल जिन्हें आपने सावधानीपूर्वक "कैप्चर" किया था, अचानक कलेक्शन के अंतिम तत्व को संदर्भित कर रहे हैं। परिचित लग रहा है?

\n\n

Stack Overflow पर प्रश्न "Is there a reason for C#'s reuse of the variable in a foreach?" ने हजारों व्यूज़ और कई टिप्पणियाँ एकत्रित की हैं। यह केवल एक शैक्षणिक रुचि नहीं है - यह एक व्यावहारिक जाल है जिसमें अनुभवी डेवलपर्स भी फंस जाते हैं। इस लेख में हम समझेंगे कि C# ऐसा क्यों करता है, यह भाषा के संस्करणों के साथ कैसे बदला, और सबसे महत्वपूर्ण बात, इससे जुड़ी त्रुटियों से कैसे बचा जाए।

\n\n

हम सरल भाषा में, कोड उदाहरणों के साथ बात करेंगे, ताकि C# में एक नौसिखिया भी समस्या का सार समझ सके। चलिए शुरू करते हैं!

\n\n

समस्या का सार: "वेरिएबल का पुन: उपयोग" का क्या अर्थ है?

\n\n

C# में क्लासिक foreach लूप में (संस्करण 5.0 से पहले), इटरेशन वेरिएबल को पूरे लूप के लिए एक बार घोषित किया जाता था, न कि प्रत्येक इटरेशन के लिए नया बनाया जाता था। इसका मतलब है कि यदि आप इस वेरिएबल को क्लोज़र में कैप्चर करते हैं (उदाहरण के लिए, lambda एक्सप्रेशन या अनाम विधि में), तो सभी क्लोज़र एक ही वेरिएबल को संदर्भित करेंगे।

\n\n

आइए समस्या के एक क्लासिक उदाहरण को देखें:

\n\n
var actions = new List<Action>();\nvar numbers = new List<int> { 1, 2, 3, 4, 5 };\n\nforeach (var number in numbers)\n{\n    actions.Add(() => Console.WriteLine(number));\n}\n\n// सभी क्रियाएँ चलाएँ\nforeach (var action in actions)\n{\n    action();\n}\n
\n\n

प्रश्न: यह कोड क्या आउटपुट करेगा? सहज रूप से ऐसा लगता है कि 1, 2, 3, 4, 5। लेकिन वास्तव में (C# 5 से पहले) यह 5, 5, 5, 5, 5 आउटपुट करेगा।

\n\n

क्यों? क्योंकि वेरिएबल number पूरे लूप के दौरान एक ही उदाहरण में मौजूद है। जब लूप समाप्त होता है, तो इसका मान अंतिम तत्व (5) के बराबर होता है। सभी lambdas ने इस वेरिएबल के संदर्भ को कैप्चर किया, न कि निर्माण के समय इसके मान को।

\n\n

समझने के लिए एक सादृश्य

\n\n

कल्पना करें कि आपके पास एक बक्सा (वेरिएबल) है, और आप उसमें एक-एक करके अलग-अलग वस्तुएँ (मान) रखते हैं। आप अपने दोस्तों (lambdas) को वस्तुएँ नहीं, बल्कि बक्से की चाबी देते हैं। जब दोस्त अपनी वस्तुएँ लेने आते हैं, तो वे बक्सा खोलते हैं, और उसमें वह अंतिम वस्तु होती है जो आपने रखी थी। पुराना foreach इसी तरह काम करता है।

\n\n

ऐतिहासिक संदर्भ: ऐसा क्यों किया गया?

\n\n

कई शुरुआती पूछते हैं: "यह तो एक बग है! ऐसा क्यों किया गया?" वास्तव में, यह C# डेवलपर्स का एक सचेत निर्णय था, जो प्रदर्शन और संकलन की सरलता पर आधारित था।

\n\n
    \n
  • प्रदर्शन: प्रत्येक इटरेशन पर एक नया वेरिएबल बनाना अतिरिक्त मेमोरी आवंटन है। अधिकांश सरल लूप के लिए यह महत्वपूर्ण नहीं है, लेकिन उच्च-लोड सिस्टम में प्रत्येक माइक्रो-ऑप्टिमाइज़ेशन महत्वपूर्ण है।
  • \n
  • for के साथ संगतता: for लूप में भी वेरिएबल एक बार घोषित किया जाता है। डेवलपर्स एकरूपता बनाए रखना चाहते थे।
  • \n
  • कंपाइलर की सरलता: पुराना दृष्टिकोण कंपाइलर में लागू करना आसान था। लेकिन इसकी कीमत प्रोग्रामर के लिए अस्पष्ट व्यवहार था।
  • \n
\n\n

हालाँकि, समुदाय ने जल्दी ही इस "फीचर" का पता लगाया और बग रिपोर्ट लिखना शुरू कर दिया। Stack Overflow पर प्रश्न "Is there a reason for C#'s reuse of the variable in a foreach?" सबसे लोकप्रिय में से एक बन गया। Microsoft ने सुना, और C# 5.0 (2012) में व्यवहार बदल दिया गया।

\n\n

C# 5.0 में क्या बदला?

\n\n

C# 5.0 से शुरू करके, कंपाइलर foreach लूप के प्रत्येक इटरेशन के लिए एक नया वेरिएबल बनाता है। अब ऊपर दिया गया उदाहरण सहज रूप से सही ढंग से काम करेगा और 1, 2, 3, 4, 5 आउटपुट करेगा।

\n\n
// यह                

ब्लॉग

पुस्तक अनुशंसाएँ