C # स्ट्रिंग संदर्भ प्रकार?


164

मुझे पता है कि C # में "स्ट्रिंग" एक संदर्भ प्रकार है। यह MSDN पर है। हालाँकि, यह कोड तब तक काम नहीं करता है, जैसा कि तब किया जाना चाहिए:

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(string test)
    {
        test = "after passing";
    }
}

आउटपुट "पास होने से पहले" "पास होने के बाद" होना चाहिए क्योंकि मैं स्ट्रिंग को एक पैरामीटर के रूप में पारित कर रहा हूं और यह एक संदर्भ प्रकार है, दूसरा आउटपुट स्टेटमेंट यह पहचानना चाहिए कि टेस्टी विधि में पाठ बदल गया है। हालाँकि, मुझे "पास होने से पहले" "पास करने से पहले" यह प्रतीत होता है कि यह रिफ द्वारा नहीं बल्कि मूल्य से पारित किया गया है। मैं समझता हूं कि तार अपरिवर्तनीय हैं, लेकिन मैं यह नहीं देखता कि यह कैसे समझा जाएगा कि यहां क्या चल रहा है। मैं क्या खो रहा हूँ? धन्यवाद।


नीचे जॉन द्वारा संदर्भित लेख देखें। आपके द्वारा उल्लिखित व्यवहार को C ++ पॉइंटर्स द्वारा भी पुन: प्रस्तुत किया जा सकता है।
शीश

MSDN में भी बहुत अच्छी व्याख्या ।
दिमि_पेल

जवाबों:


211

स्ट्रिंग का संदर्भ मूल्य द्वारा पारित किया गया है। मूल्य के संदर्भ को संदर्भित करने और किसी वस्तु को संदर्भ द्वारा पारित करने के बीच एक बड़ा अंतर है। यह दुर्भाग्यपूर्ण है कि शब्द "संदर्भ" दोनों मामलों में उपयोग किया जाता है।

यदि आप संदर्भ द्वारा स्ट्रिंग संदर्भ पास करते हैं , तो यह आपकी अपेक्षा के अनुसार काम करेगा:

using System;

class Test
{
    public static void Main()
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(ref test);
        Console.WriteLine(test);
    }

    public static void TestI(ref string test)
    {
        test = "after passing";
    }
}

अब आपको उस वस्तु में परिवर्तन करने के बीच अंतर करने की आवश्यकता है जिसे एक संदर्भ संदर्भित करता है, और किसी भिन्न वस्तु को संदर्भित करने के लिए एक चर (जैसे एक पैरामीटर) में परिवर्तन करना। हम स्ट्रिंग में परिवर्तन नहीं कर सकते क्योंकि तार अपरिवर्तनीय हैं, लेकिन हम इसे StringBuilderइसके बजाय प्रदर्शित कर सकते हैं :

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder test = new StringBuilder();
        Console.WriteLine(test);
        TestI(test);
        Console.WriteLine(test);
    }

    public static void TestI(StringBuilder test)
    {
        // Note that we're not changing the value
        // of the "test" parameter - we're changing
        // the data in the object it's referring to
        test.Append("changing");
    }
}

देखें पैरामीटर गुजर पर मेरे लेख अधिक जानकारी के लिए।


2
सहमत हैं, बस यह स्पष्ट करना चाहते हैं कि रेफ मॉडिफायर का उपयोग गैर-संदर्भ प्रकारों के लिए भी काम करता है अर्थात दोनों बहुत अलग अवधारणाएं हैं।
इगलासियस

2
@Jon Skeet ने आपके लेख में सिडेन को बहुत पसंद किया। आपको referencedअपने उत्तर के रूप में ऐसा करना चाहिए
Nishish Inpursuit ofhappiness

36

यदि हमें प्रश्न का उत्तर देना है: स्ट्रिंग एक संदर्भ प्रकार है और यह एक संदर्भ के रूप में व्यवहार करता है। हम एक पैरामीटर पास करते हैं जो वास्तविक स्ट्रिंग नहीं, के लिए एक संदर्भ रखता है। समस्या फ़ंक्शन में है:

public static void TestI(string test)
{
    test = "after passing";
}

पैरामीटर testस्ट्रिंग का संदर्भ रखता है लेकिन यह एक प्रति है। हमारे पास स्ट्रिंग की ओर इशारा करते हुए दो चर हैं। और क्योंकि स्ट्रिंग्स के साथ कोई भी ऑपरेशन वास्तव में एक नई वस्तु बनाते हैं, हम नए स्ट्रिंग को इंगित करने के लिए अपनी स्थानीय प्रतिलिपि बनाते हैं। लेकिन मूल testचर नहीं बदला गया है।

डालने के लिए सुझाए गए उपाय refफंक्शन डिक्लेरेशन और इनवोकेशन वर्क में क्योंकि हम testवैरिएबल की वैल्यू पास नहीं करेंगे बल्कि इसके लिए सिर्फ एक रेफरेंस पास करेंगे। इस प्रकार फ़ंक्शन के अंदर कोई भी परिवर्तन मूल चर को प्रतिबिंबित करेगा।

मैं अंत में दोहराना चाहता हूं: स्ट्रिंग एक संदर्भ प्रकार है, लेकिन चूंकि इसकी अपरिवर्तनीय रेखा test = "after passing";वास्तव में एक नई वस्तु बनाती है और चर की हमारी प्रतिलिपि testनए स्ट्रिंग को इंगित करने के लिए बदल जाती है।


25

जैसा कि दूसरों ने कहा है, String .NET में टाइप अपरिवर्तनीय है और यह संदर्भ मूल्य द्वारा पारित किया गया है।

मूल कोड में, जैसे ही यह लाइन निष्पादित होती है:

test = "after passing";

तब मूल वस्तु का जिक्रtest नहीं किया जाता है । हमने एक नई वस्तु बनाई है और उसे सौंपा है Stringtest उस ऑब्जेक्ट को प्रबंधित हीप पर संदर्भित करने के लिए किया है।

मुझे लगता है कि बहुत से लोग यहां पर फंस गए हैं क्योंकि उन्हें याद दिलाने के लिए कोई औपचारिक औपचारिक निर्माता नहीं है। इस मामले में, यह पर्दे के पीछे से हो रहा हैString प्रकार का निर्माण में भाषा का समर्थन है।

इसलिए, यही कारण है कि विधि testके दायरे के बाहर परिवर्तन दिखाई नहीं दे रहा है TestI(string)- हमने संदर्भ को मूल्य से पारित कर दिया है और अब यह मान बदल गया है! लेकिन अगर Stringसंदर्भ को संदर्भ द्वारा पारित किया गया था, तो जब संदर्भ बदल गया तो हम इसे TestI(string)विधि के दायरे से बाहर देखेंगे ।

इस मामले में या तो रेफरी या आउट कीवर्ड की जरूरत है। मुझे लगता है कि outइस विशेष स्थिति के लिए कीवर्ड थोड़ा बेहतर हो सकता है।

class Program
{
    static void Main(string[] args)
    {
        string test = "before passing";
        Console.WriteLine(test);
        TestI(out test);
        Console.WriteLine(test);
        Console.ReadLine();
    }

    public static void TestI(out string test)
    {
        test = "after passing";
    }
}

Ref = initialised function के बाहर, out = initialized of function, या दूसरे शब्दों में; रेफ दो तरह से है, केवल आउट-आउट है। तो निश्चित रूप से रेफ का उपयोग किया जाना चाहिए।
पॉल ज़हरा

@PaZZahra: outकोड को संकलित करने के लिए विधि के भीतर सौंपा जाना चाहिए। refऐसी कोई आवश्यकता नहीं है। साथ ही outमापदंडों को विधि के बाहर शुरू किया जाता है - इस उत्तर में कोड एक प्रतिरूप है।
डेरेक डब्ल्यू

स्पष्ट करना चाहिए - outमापदंडों को विधि के बाहर शुरू किया जा सकता है, लेकिन करने की आवश्यकता नहीं है। इस स्थिति में, हम .NET में प्रकार outकी प्रकृति के बारे में एक बिंदु प्रदर्शित करने के लिए पैरामीटर को इनिशियलाइज़ करना चाहते हैं string
डेरेक डब्ल्यू

9

वास्तव में यह उस मामले के लिए किसी भी वस्तु के लिए एक ही होता है अर्थात एक संदर्भ प्रकार और संदर्भ से गुजरना c # में 2 अलग-अलग चीजें हैं।

यह काम करेगा, लेकिन यह इस प्रकार की परवाह किए बिना लागू होता है:

public static void TestI(ref string test)

इसके अलावा एक संदर्भ प्रकार स्ट्रिंग होने के बारे में, यह भी एक विशेष एक है। इसका डिज़ाइन अपरिवर्तनीय है, इसलिए इसके सभी तरीके उदाहरण को संशोधित नहीं करेंगे (वे एक नया लौटाते हैं)। प्रदर्शन के लिए इसमें कुछ अतिरिक्त चीजें भी हैं।


7

मूल्य-प्रकार, पास-पास-मूल्य, संदर्भ-प्रकार और पास-पास-संदर्भ के बीच अंतर के बारे में सोचने का एक अच्छा तरीका यहां दिया गया है:

एक चर एक कंटेनर है।

एक मान-प्रकार चर में एक उदाहरण होता है। एक संदर्भ-प्रकार के चर में कहीं और संग्रहीत उदाहरण के लिए एक सूचक होता है।

एक मूल्य-प्रकार चर को संशोधित करना उस उदाहरण को उत्परिवर्तित करता है जिसमें यह होता है। एक संदर्भ-प्रकार चर को संशोधित करना उस उदाहरण को उत्परिवर्तित करता है जो इसे इंगित करता है।

अलग-अलग संदर्भ-प्रकार चर समान उदाहरण को इंगित कर सकते हैं। इसलिए, उसी उदाहरण को किसी भी चर के माध्यम से उत्परिवर्तित किया जा सकता है जो इसे इंगित करता है।

एक पारित-दर-मूल्य तर्क सामग्री की एक नई प्रति के साथ एक नया कंटेनर है। एक पारित-दर-संदर्भ तर्क अपनी मूल सामग्री के साथ मूल कंटेनर है।

जब एक मान-प्रकार का तर्क पास-पास-मूल्य होता है: तर्क की सामग्री को पुन: असाइन करने से दायरे के बाहर कोई प्रभाव नहीं पड़ता है, क्योंकि कंटेनर अद्वितीय है। तर्क को संशोधित करने से दायरे के बाहर कोई प्रभाव नहीं पड़ता है, क्योंकि उदाहरण एक स्वतंत्र प्रति है।

जब एक संदर्भ-प्रकार का तर्क पास-पास-मान होता है: तर्क की सामग्री को पुन: असाइन करने से दायरे के बाहर कोई प्रभाव नहीं पड़ता है, क्योंकि कंटेनर अद्वितीय है। तर्क की सामग्री को संशोधित करना बाहरी दायरे को प्रभावित करता है, क्योंकि कॉपी किए गए पॉइंटर एक साझा उदाहरण की ओर इशारा करते हैं।

जब कोई तर्क पारित-दर-संदर्भ होता है: तर्क की सामग्री को पुन: सौंपना बाहरी दायरे को प्रभावित करता है, क्योंकि कंटेनर साझा किया जाता है। तर्क की सामग्री को संशोधित करना बाहरी दायरे को प्रभावित करता है, क्योंकि सामग्री साझा की जाती है।

निष्कर्ष के तौर पर:

एक स्ट्रिंग चर एक संदर्भ-प्रकार चर है। इसलिए, इसमें कहीं और संग्रहीत उदाहरण के लिए एक सूचक होता है। जब पास-बाय-वैल्यू, इसके पॉइंटर को कॉपी किया जाता है, तो एक स्ट्रिंग तर्क को संशोधित करना साझा उदाहरण को प्रभावित करना चाहिए। हालाँकि, एक स्ट्रिंग उदाहरण में कोई परिवर्तनशील गुण नहीं है, इसलिए एक स्ट्रिंग तर्क को वैसे भी संशोधित नहीं किया जा सकता है। जब पास-पास-संदर्भ होता है, तो पॉइंटर का कंटेनर साझा किया जाता है, इसलिए पुन: असाइनमेंट अभी भी बाहरी दायरे को प्रभावित करेगा।


6

" एक तस्वीर एक हजार शब्दों के लायक है "।

मेरे पास यहां एक सरल उदाहरण है, यह आपके मामले के समान है।

string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc

यह हुआ था:

यहां छवि विवरण दर्ज करें

  • पंक्ति 1 और 2: s1और s2चर एक ही "abc"स्ट्रिंग ऑब्जेक्ट का संदर्भ देते हैं ।
  • पंक्ति 3: क्योंकि तार अपरिवर्तनीय हैं , इसलिए "abc"स्ट्रिंग ऑब्जेक्ट स्वयं को संशोधित नहीं करता है (इन "def"), लेकिन "def"इसके बजाय एक नया स्ट्रिंग ऑब्जेक्ट बनाया जाता है, और फिर इसका s1संदर्भ देता है।
  • पंक्ति 4: s2अभी भी "abc"स्ट्रिंग ऑब्जेक्ट को संदर्भित करता है, इसलिए यह आउटपुट है।

5

उपरोक्त उत्तर सहायक हैं, मैं सिर्फ एक उदाहरण जोड़ना चाहूंगा जो मुझे लगता है कि स्पष्ट रूप से प्रदर्शित होता है जब हम रेफरी कीवर्ड के बिना पैरामीटर पास करते हैं, तब भी जब वह पैरामीटर एक संदर्भ प्रकार होता है:

MyClass c = new MyClass(); c.MyProperty = "foo";

CNull(c); // only a copy of the reference is sent 
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c); 
Console.WriteLine(c.MyProperty); // bar


private void CNull(MyClass c2)
        {          
            c2 = null;
        }
private void CPropertyChange(MyClass c2) 
        {
            c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
        }

1
इस स्पष्टीकरण ने मेरे लिए सबसे अच्छा काम किया। इसलिए मूल रूप से हम सब कुछ इस तथ्य के बावजूद मान लेते हैं कि चर स्वयं मूल्य या संदर्भ प्रकार है जब तक कि हम रेफ कीवर्ड (या बाहर) का उपयोग नहीं करते हैं। यह हमारे दिन-प्रतिदिन के कोडिंग के साथ प्रमुख नहीं है क्योंकि हम आम तौर पर अपनी वस्तुओं को एक विधि के अंदर या किसी अन्य उदाहरण में अशक्त करने के लिए सेट नहीं करते हैं जहां वे पारित किए गए थे, बल्कि हम उनके गुणों को निर्धारित करते हैं या उनके तरीकों को कॉल करते हैं। "स्ट्रिंग" के मामले में, इसे एक नए उदाहरण में सेट करना हर समय होता है, लेकिन नया दिखाई नहीं देता है और यह अप्रशिक्षित आंख को गलत व्याख्या देता है। गलत होने पर मुझे सुधारो।
5: Г И О И О

3

जिज्ञासु मन के लिए और बातचीत को पूरा करने के लिए: हाँ, स्ट्रिंग एक संदर्भ प्रकार है :

unsafe
{
     string a = "Test";
     string b = a;
     fixed (char* p = a)
     {
          p[0] = 'B';
     }
     Console.WriteLine(a); // output: "Best"
     Console.WriteLine(b); // output: "Best"
}

लेकिन ध्यान दें कि यह परिवर्तन केवल एक असुरक्षित ब्लॉक में काम करता है ! क्योंकि स्ट्रिंग्स अपरिवर्तनीय हैं (MSDN से):

ऑब्जेक्ट के बनने के बाद एक स्ट्रिंग ऑब्जेक्ट की सामग्री को नहीं बदला जा सकता है, हालांकि सिंटैक्स यह प्रकट करता है जैसे कि आप ऐसा कर सकते हैं। उदाहरण के लिए, जब आप इस कोड को लिखते हैं, तो संकलक वास्तव में वर्णों के नए अनुक्रम को रखने के लिए एक नया स्ट्रिंग ऑब्जेक्ट बनाता है, और उस नए ऑब्जेक्ट को b को असाइन किया जाता है। स्ट्रिंग "h" तब कचरा संग्रहण के लिए योग्य है।

string b = "h";  
b += "ello";  

और ध्यान रखें कि:

हालाँकि, स्ट्रिंग एक संदर्भ प्रकार है, समानता ऑपरेटरों ( ==और !=) को स्ट्रिंग ऑब्जेक्ट्स के मूल्यों की तुलना करने के लिए परिभाषित किया गया है, संदर्भ नहीं।


0

मेरा मानना ​​है कि आपका कोड निम्नलिखित के अनुरूप है, और आपको उस मूल्य के बदले जाने की उम्मीद नहीं करनी चाहिए, जो यहाँ नहीं होगा:

 public static void Main()
 {
     StringWrapper testVariable = new StringWrapper("before passing");
     Console.WriteLine(testVariable);
     TestI(testVariable);
     Console.WriteLine(testVariable);
 }

 public static void TestI(StringWrapper testParameter)
 {
     testParameter = new StringWrapper("after passing");

     // this will change the object that testParameter is pointing/referring
     // to but it doesn't change testVariable unless you use a reference
     // parameter as indicated in other answers
 }

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.