जवाबों:
एक संदर्भ पारित किया गया है; हालाँकि, यह तकनीकी रूप से संदर्भ से पारित नहीं है । यह एक सूक्ष्म, लेकिन बहुत महत्वपूर्ण अंतर है। निम्नलिखित कोड पर विचार करें:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
यहां क्या होता है, इसे समझने के लिए आपको तीन बातें जानने की जरूरत है:
strMainसंदर्भ द्वारा पारित नहीं किया गया है। यह एक संदर्भ प्रकार है, लेकिन संदर्भ स्वयं मूल्य द्वारा पारित किया जाता है । जब भी आप refकीवर्ड के बिना एक पैरामीटर पास करते हैं ( outमापदंडों की गिनती नहीं करते हैं ), तो आपने मूल्य से कुछ पारित किया है।तो इसका मतलब यह होना चाहिए कि आप ... एक संदर्भ मूल्य से गुजर रहे हैं। चूंकि यह एक संदर्भ प्रकार है, केवल संदर्भ को स्टैक पर कॉपी किया गया था। लेकिन इसका क्या मतलब है?
C # चर या तो संदर्भ प्रकार या मान प्रकार हैं । सी # मानकों या तो कर रहे हैं संदर्भ द्वारा पारित या मूल्य द्वारा पारित । शब्दावली यहां एक समस्या है; एक ही चीज़ की तरह ये ध्वनि, लेकिन वे नहीं हैं।
यदि आप किसी भी प्रकार का पैरामीटर पास करते हैं, और आप refकीवर्ड का उपयोग नहीं करते हैं , तो आप इसे मूल्य से पास कर चुके हैं। यदि आपने इसे मूल्य से पास कर दिया है, तो आप वास्तव में जो पास किया है वह एक कॉपी था। लेकिन अगर पैरामीटर एक संदर्भ प्रकार था, तो आपके द्वारा कॉपी की गई चीज संदर्भ थी, न कि वह जो इंगित कर रही थी।
यहाँ Mainविधि की पहली पंक्ति है :
string strMain = "main";
हमने इस पंक्ति में दो चीजें बनाई हैं: एक स्ट्रिंग जिसमें mainमेमोरी में कहीं स्टोर की गई है, और एक संदर्भ चर जिसे strMainइंगित किया जाता है।
DoSomething(strMain);
अब हम उस संदर्भ को पास करते हैं DoSomething। हमने इसे मान से पास कर दिया है, इसलिए इसका मतलब है कि हमने एक प्रति बना ली है। यह एक संदर्भ प्रकार है, इसलिए इसका अर्थ है कि हमने संदर्भ की प्रतिलिपि बनाई है, न कि स्ट्रिंग की। अब हमारे पास दो संदर्भ हैं जो प्रत्येक मेमोरी में एक ही मान के लिए इंगित करते हैं।
यहाँ DoSomethingविधि का शीर्ष है :
void DoSomething(string strLocal)
कोई refकीवर्ड नहीं , इसलिए strLocalऔर strMainदो अलग-अलग संदर्भ एक ही मूल्य पर इंगित कर रहे हैं। अगर हम फिर से भरोसा strLocal...
strLocal = "local";
... हमने संग्रहीत मूल्य नहीं बदला है; हमने बुलाया संदर्भ लिया strLocalऔर इसे एक नए स्ट्रिंग में लक्षित किया। strMainजब हम ऐसा करते हैं तो क्या होता है? कुछ भी तो नहीं। यह अभी भी पुराने तार की ओर इशारा कर रहा है।
string strMain = "main"; // Store a string, create a reference to it
DoSomething(strMain); // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main"
चलो एक दूसरे के लिए परिदृश्य बदलते हैं। कल्पना करें कि हम स्ट्रिंग्स के साथ काम नहीं कर रहे हैं, लेकिन कुछ परस्पर संदर्भ प्रकार, जैसे आपके द्वारा बनाई गई कक्षा।
class MutableThing
{
public int ChangeMe { get; set; }
}
यदि आप उस ऑब्जेक्ट के संदर्भ का अनुसरण करते हैं objLocal, जो आपको इंगित करता है, तो आप इसके गुणों को बदल सकते हैं:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
MutableThingस्मृति में अभी भी केवल एक ही है , और प्रतिलिपि किए गए संदर्भ और मूल संदर्भ दोनों अभी भी इसे इंगित करते हैं। खुद के गुण MutableThingबदल गए हैं :
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain
DoSomething(objMain); // now it's 0 on objLocal
Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain
}
आह, लेकिन तार अपरिवर्तनीय हैं! ChangeMeसेट करने के लिए कोई संपत्ति नहीं है । आप strLocal[3] = 'H'सी # में नहीं कर सकते हैं जैसे आप सी-स्टाइल charसरणी के साथ कर सकते हैं ; आपको इसके बजाय एक पूरी नई स्ट्रिंग बनानी होगी। बदलने strLocalका एकमात्र तरीका संदर्भ को दूसरे स्ट्रिंग पर इंगित करना है, और इसका मतलब है कि आप जो कुछ भी कर strLocalसकते हैं वह प्रभावित नहीं कर सकता है strMain। मान अपरिवर्तनीय है, और संदर्भ एक प्रति है।
यह साबित करने के लिए कि अंतर क्या है, जब आप संदर्भ द्वारा एक संदर्भ देते हैं , तो यहां क्या होता है :
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
इस बार, स्ट्रिंग Mainवास्तव में बदल जाती है क्योंकि आपने इसे स्टैक पर कॉपी किए बिना संदर्भ पारित किया है।
इसलिए, भले ही तार संदर्भ प्रकार हों, लेकिन उन्हें मान से गुजरने का मतलब है कि कॉलली में जो कुछ भी होता है वह कॉलर में स्ट्रिंग को प्रभावित नहीं करेगा। लेकिन जब से वे कर रहे हैं संदर्भ प्रकार, आप जब आप इसे चारों ओर पास करना चाहते हैं स्मृति में पूरी स्ट्रिंग कॉपी करने के लिए नहीं है।
refकीवर्ड की आवश्यकता है । यह साबित करने के लिए कि संदर्भ से गुजरने से फर्क पड़ता है, इस डेमो को देखें: rextester.com/WKBG5978
refकीवर्ड की उपयोगिता है, मैं बस यह समझाने की कोशिश कर रहा था कि कोई व्यक्ति C # में मान द्वारा संदर्भ प्रकार पास करने के बारे में क्यों सोच सकता है, ऐसा लगता है जैसे संदर्भ से गुजरने की "पारंपरिक" (यानी C) धारणा (और संदर्भ प्रकार पास करना) C के संदर्भ में # ऐसा लगता है जैसे मान द्वारा किसी संदर्भ को भेजना अधिक पसंद है)।
Foo(string bar)के रूप में सोचा जा सकता है Foo(char* bar), जबकि Foo(ref string bar)होगा Foo(char** bar)(या Foo(char*& bar)या Foo(string& bar)C ++)। ज़रूर, यह नहीं है कि आपको इसे हर रोज कैसे सोचना चाहिए, लेकिन इससे वास्तव में मुझे यह समझने में मदद मिली कि हुड के नीचे क्या हो रहा है।
C # में स्ट्रिंग्स अपरिवर्तनीय संदर्भ ऑब्जेक्ट हैं। इसका मतलब है कि उनके संदर्भ (मूल्य के अनुसार) पास हो गए हैं, और एक बार स्ट्रिंग बनाने के बाद, आप इसे संशोधित नहीं कर सकते। स्ट्रिंग (संशोधित, छंटनी किए गए संस्करण, आदि) के संशोधित संस्करण उत्पन्न करने वाले तरीके मूल स्ट्रिंग की संशोधित प्रतियां बनाते हैं ।
स्ट्रिंग्स विशेष मामले हैं। प्रत्येक उदाहरण अपरिवर्तनीय है। जब आप एक स्ट्रिंग का मूल्य बदलते हैं तो आप मेमोरी में एक नया स्ट्रिंग आवंटित कर रहे हैं।
इसलिए आपके फ़ंक्शन को केवल संदर्भ दिया जाता है, लेकिन जब स्ट्रिंग संपादित की जाती है तो यह एक नया उदाहरण बन जाता है और पुराने उदाहरण को संशोधित नहीं करता है।
Uri(वर्ग) और Guid(संरचना) भी विशेष मामले हैं। मैं यह नहीं देखता कि System.Stringअन्य अपरिवर्तनीय प्रकारों की तुलना में "वैल्यू टाइप" की तरह काम कैसे किया जाता है ... या तो क्लास या स्ट्रक्चर ओरिजिन।
Uriऔर Guidआप स्ट्रिंग स्ट्रिंग के लिए केवल एक स्ट्रिंग-शाब्दिक मान असाइन कर सकते हैं। स्ट्रिंग intपुन: असाइन किया जा रहा है , जैसा प्रतीत होता है , लेकिन यह एक वस्तु का निर्माण कर रहा है - कोई newकीवर्ड नहीं ।