C # में संदर्भ प्रकार


79

इस कोड पर विचार करें:

public class Program
{
    private static void Main(string[] args)
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        Person person2 = person1;
        person2.Name = "Shahrooz";
        Console.WriteLine(person1.Name); //Output: Shahrooz
        person2 = null;
        Console.WriteLine(person1.Name); //Output: Shahrooz
    }
}

public class Person
{
    public string Name { get; set; }
}

जाहिर है, जब बताए person1करने के लिए person2और Nameकी संपत्ति person2बदल गया है, Nameकी person1भी बदल जाएगा। person1और person2एक ही संदर्भ है।

ऐसा क्यों है कि जब person2 = null, person1चर या तो शून्य नहीं होगा?

जवाबों:


183

दोनों personऔर person2कर रहे हैं संदर्भ , एक ही वस्तु है। लेकिन ये अलग-अलग संदर्भ हैं। तो जब तुम दौड़ रहे हो

person2 = null;

आप केवल संदर्भ बदल रहे हैं person2, संदर्भ छोड़कर personऔर संबंधित वस्तु अपरिवर्तित है।

मुझे यह समझाने का सबसे अच्छा तरीका लगता है कि यह एक सरलीकृत चित्रण के साथ है। यहाँ स्थिति पहले जैसी दिख रही है person2 = null:

अशक्त असाइनमेंट से पहले

और यहाँ अशक्त असाइनमेंट के बाद की तस्वीर है :

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

जैसा कि आप देख सकते हैं, दूसरी तस्वीर के person2संदर्भ में कुछ भी नहीं (या null, सख्ती से बोलना, संदर्भ के बाद से कुछ भी नहीं है और संदर्भ nullअलग-अलग स्थितियां हैं, रून एफएस द्वारा टिप्पणी देखें ), जबकि personअभी भी एक मौजूदा वस्तु का संदर्भ है।


22
दूसरे शब्दों में, व्यक्ति और person2 कुछ को इंगित करता है, तो person2 शून्य करने के लिए कुछ भी नहीं करने के लिए इंगित करता है।
rro

2
@ShahroozJefri oz मूल रूप से, person1और person22 अलग-अलग व्यवसाय कार्ड हैं जिनमें एक पता होता है। यदि आप करते हैं person1 = person2तो person1अब की एक प्रति है person2। वे अभी भी अलग-अलग व्यवसाय कार्ड हैं, लेकिन वे दोनों एक ही पते को एक ही वस्तु की ओर इंगित करते हैं । वस्तु अपने आप नहीं बदलती।
नोलोनार

मैं person2दूसरी तस्वीर में तीर को हटा दूंगा क्योंकि यह कुछ भी नहीं संदर्भित करता है - यह झूलने वाला संदर्भ नहीं है।
समीर सिंह

@SameerSingh, किया, प्रतिक्रिया के लिए धन्यवाद। मैं शुरू में ऐसा ही सोच रहा था, लेकिन किसी कारण से मेरा मन बदल गया।
आंद्रेई

2
संदर्भों का उपयोग करते समय, एक वैरिएबल बनाने और इसे किसी अन्य पॉइंटर को असाइन करने पर, एलियास नामक एक चीज़ का निर्माण होता है, जहाँ, हम कहते हैं, 3 चर, एक ही मेमोरी स्थान की ओर इशारा करते हैं। जब आप उनमें से किसी एक चर को शून्य करते हैं, तो उस मेमोरी स्थान की ओर इशारा करते हुए एक चर कम होता है, जब सभी चर NULL को इंगित करते हैं, GC उस स्थान को नया चर आवंटित करने के लिए साफ करता है, कम से कम जावा में GC उस तरह से काम करता है जैसे D:
नेमिसिसडूम

55

भंडारण में कुछ स्थान पर विचार करें person1और person2 इंगित करें। पहले चरण में, केवल person1स्टोरेज से ऑब्जेक्ट person2का एड्रेस पकड़ रहा है और बाद में स्टोरेज से ऑब्जेक्ट की मेमोरी लोकेशन का एड्रेस पकड़ रहा है। बाद में जब आप असाइन nullकरते हैं person2, तो person1अप्रभावित रहता है। यही कारण है कि आप परिणाम देखते हैं।

आप पढ़ सकते हैं: यूसुफ अलबहारी से मूल्य बनाम संदर्भ प्रकार

संदर्भ प्रकारों के साथ, हालाँकि, एक ऑब्जेक्ट को मेमोरी में बनाया जाता है, और फिर एक अलग संदर्भ के माध्यम से संभाला जाता है - बल्कि एक पॉइंटर की तरह।

मैं निम्नलिखित चित्र का उपयोग करके उसी अवधारणा को चित्रित करने का प्रयास करूंगा।

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

टाइप व्यक्ति और person1संदर्भ (पॉइंटर) के एक नए ऑब्जेक्ट को स्टोरेज में मेमोरी लोकेशन की ओर इशारा करता है।

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

एक नया संदर्भ (पॉइंटर) बनाया गया person2जो भंडारण में उसी ओर इशारा कर रहा है।

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

ऑब्जेक्ट प्रॉपर्टी के नाम को नए मूल्य में बदल दिया, person2क्योंकि दोनों संदर्भ एक ही ऑब्जेक्ट, Console.WriteLine(person1.Name);आउटपुट पर इंगित कर रहे हैंShahrooz

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

बताए जाने के बाद nullकरने के लिएperson2संदर्भ , यह कुछ भी नहीं की ओर इशारा करेगा, लेकिन person1अभी भी वस्तु के संदर्भ को पकड़े हुए है।

(अंत में मेमोरी मैनेजमेंट के लिए आपको द स्टैक इज एन इंप्लीमेंटेशन डिटेल, पार्ट वन और द स्टैक इज एन इंप्लीमेंटेशन डिटेल, पार्ट टू एरिक लिपर्ट से देखना चाहिए )


3
"हीप" का कोई भी संदर्भ एक अप्रासंगिक कार्यान्वयन विवरण है। मुझे यकीन नहीं है कि यह सच है, यह निश्चित रूप से होने की आवश्यकता नहीं है।
15:14 बजे jmoreno

@ जोरमेनो, यही कारण है कि अंतिम पैराग्राफ, वास्तव में मैंने जोसेफ अलबहारी के लेख में शैली का पालन किया, उत्तर के शीर्ष पर जुड़ा हुआ है
हबीब

4
@jmoreno, जहां तक ​​इस जवाब का सवाल है, तो यह सही है कि प्रकार की वस्तु personको ढेर और उसके संदर्भों पर संग्रहीत किया जाएगा person1और person2स्टैक पर होगा। लेकिन मैं इसके कार्यान्वयन विस्तार के बारे में अधिक सहमत हूं। लेकिन जवाब में स्टैक और ढेर होने (जैसे अल्बहारी का लेख) इसे और अधिक स्पष्ट आईएमओ बना देगा।
हबीब

1
@ जोरमेनो और हेबिन: मैंने भंडारण के साथ हीप के उपयोग को प्रतिस्थापित कर दिया है, विशेष रूप से भंडारण तंत्र के उपयोग, या गैर-उपयोग * को आमतौर पर हीप के रूप में जाना जाता है अप्रासंगिक है।
पीटर गेकर्न्स 22

1
शानदार जवाब लेकिन यह पूछे जाने वाले सवाल के लिए थोड़ा ऊपर है।
रेजर

14

आप person2संदर्भ में बदल गए हैं null, लेकिन person1वहां संदर्भित नहीं है।

मेरा मतलब यह है कि यदि हम असाइनमेंट को देखते हैं person2और person1उससे पहले देखते हैं तो दोनों एक ही ऑब्जेक्ट को संदर्भित करते हैं। फिर आप असाइन करते हैं person2 = null, इसलिए व्यक्ति 2 अब एक अलग प्रकार का संदर्भ दे रहा है। यह उस ऑब्जेक्ट को नहीं हटाता है person2जिसे संदर्भित किया गया था।

मैंने इसे स्पष्ट करने के लिए यह gif बनाया है:

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


13

क्योंकि आपने संदर्भ सेट कर दिया है null

जब आप एक संदर्भ सेट करते हैं null, तो संदर्भ स्वयं होता हैnull .. न कि वह संदर्भ।

उन्हें एक चर के रूप में सोचो जो 0. से ऑफसेट रखता personहै, जिसका मूल्य 120 person2है। मूल्य 120 है। ऑफसेट 120 का डेटा Personऑब्जेक्ट है। जब आप ऐसा करते हैं:

person2 = null;

..you're प्रभावी ढंग से करते हुए कहा person2 = 0;। हालांकि, personअभी भी मूल्य 120 है।


इसलिए व्यक्ति 2 और व्यक्ति का एक ही संदर्भ नहीं है?

वे एक ही वस्तु का संदर्भ देते हैं .. लेकिन वे अलग-अलग संदर्भ हैं। यह copy-by-valueशब्दार्थ के लिए आता है । आप संदर्भ का मान कॉपी कर रहे हैं।
साइमन व्हाइटहेड

4

दोनों personऔर एक ही वस्तु को person2 इंगित करते हैं। इसलिए जब आप दोनों में से किसी एक का नाम बदलते हैं, तो दोनों बदल जाएंगे (क्योंकि वे स्मृति में समान संरचना की ओर इशारा करते हैं)।

लेकिन जब आप सेट person2करते हैं null, तो आप person2एक अशक्त सूचक बनाते हैं , इसलिए यह किसी भी वस्तु की ओर इशारा नहीं करता है person। इसे नष्ट करने के लिए यह स्वयं वस्तु को कुछ भी नहीं कर सकता है, और जब से personयह वस्तु को संदर्भित करता है, तब तक यह कचरा संग्रह द्वारा मारा नहीं जाता है।

यदि आप सेट भी करते हैं person = null, और आपके पास ऑब्जेक्ट के लिए कोई अन्य संदर्भ नहीं है, तो यह अंततः कचरा कलेक्टर द्वारा हटा दिया जाएगा।


2

person1और person2उसी मेमोरी पते की ओर इशारा करते हैं। जब आप अशक्त करते हैं person2, तो आप संदर्भ को शून्य करते हैं, मेमोरी एड्रेस को नहीं, इसलिए person1उस मेमोरी एड्रेस को रिफ़र करना जारी रखता है, यही कारण है। यदि आप Classs Personएक में बदल जाते हैं Struct, तो व्यवहार बदल जाएगा।


वे एक ही मेमोरी पते (या उस मामले के लिए किसी भी पते) को इंगित या नहीं कर सकते हैं। क्या महत्वपूर्ण है कि वे एक ही वस्तु की पहचान करें । कुछ प्रकार के समवर्ती कचरा संग्राहक पर, यह संभव हो सकता है कि जब किसी वस्तु को स्थानांतरित किया जा रहा हो तो कुछ संदर्भों में पुराना पता हो सकता है, जबकि अन्य नए को पहचानते हैं [कोड जो या तो पते को लिखता है, जब तक कि सभी संदर्भ अपडेट नहीं किए जाते हैं, और ब्लॉक करना पड़ सकता है, और कोड जो पते की तुलना करते हैं, यह देखना होगा कि क्या एक पता "चालू" था और दूसरा नहीं था और यदि ऐसा है, तो पुराने के साथ जुड़ा "नया" पता खोजें।]
सुपरकैट

2

मुझे ऑब्जेक्ट आईडी रखने के रूप में संदर्भ प्रकारों के बारे में सोचना सबसे सहायक लगता है। यदि किसी के पास वर्ग प्रकार का एक चर है Car, तो बयान myCar = new Car();प्रणाली को एक नई कार बनाने और उसकी आईडी की रिपोर्ट करने के लिए कहता है (मान लें कि यह ऑब्जेक्ट # 57 है); यह तब चर में "ऑब्जेक्ट # 57" डालता है myCar। अगर कोई लिखता है Car2 = myCar;, तो वह "ऑब्जेक्ट # 57" वेरिएबल Car2 में लिखता है। अगर कोई लिखता हैcar2.Color = blue; , जो सिस्टम को कार 2 (जैसे ऑब्जेक्ट # 57) द्वारा पहचानी गई कार को खोजने और नीले रंग में पेंट करने का निर्देश देता है।

केवल ऐसे ऑपरेशन जो ऑब्जेक्ट आईडी पर सीधे किए जाते हैं वे एक नई वस्तु का निर्माण करते हैं और आईडी प्राप्त करते हैं, एक "रिक्त" आईडी (यानी शून्य) प्राप्त करते हुए, एक वस्तु आईडी को एक चर या भंडारण स्थान पर कॉपी करते हैं जो इसे पकड़ सकता है, चाहे दो ऑब्जेक्ट आईडी मैच (एक ही वस्तु को देखें)। अन्य सभी अनुरोध सिस्टम को आईडी द्वारा संदर्भित वस्तु को खोजने के लिए कहते हैं और उस वस्तु पर कार्य करते हैं (बिना वैरिएबल या अन्य संस्था को प्रभावित किए हुए जो आईडी के पास है)।

.NET के मौजूदा कार्यान्वयन में, ऑब्जेक्ट चर एक कचरा एकत्र ढेर पर संग्रहीत ऑब्जेक्ट के लिए पॉइंटर्स रखने की संभावना रखते हैं, लेकिन यह एक अनपेक्षित कार्यान्वयन विवरण है क्योंकि ऑब्जेक्ट संदर्भ और किसी अन्य प्रकार के पॉइंटर के बीच एक महत्वपूर्ण अंतर है। एक पॉइंटर को आम तौर पर किसी चीज़ के स्थान का प्रतिनिधित्व करने के लिए ग्रहण किया जाता है, जिसके साथ काम करने के लिए लंबे समय तक रखा जाएगा। वस्तु संदर्भ नहीं। कोड का एक टुकड़ा पता 0x12345678 पर स्थित ऑब्जेक्ट के संदर्भ में SI रजिस्टर लोड कर सकता है, इसका उपयोग करना शुरू कर सकता है, और फिर बाधित हो सकता है जबकि कचरा कलेक्टर ऑब्जेक्ट को 0x23456789 पर ले जाता है। यह एक आपदा की तरह लग रहा होगा, लेकिन कचरा कोड के साथ जुड़े मेटाडेटा की जांच करेगा, निरीक्षण करेगा कि कोड एसआई का उपयोग उस वस्तु का पता रखने के लिए करता था जिसका वह उपयोग कर रहा था (यानी 0x12345678), उस ऑब्जेक्ट को निर्धारित करें जो 0x12345678 पर था, 0x23456789 पर ले जाया गया था, और एसआई को 0x23456789 पर वापस जाने से पहले अपडेट करने के लिए अपडेट करें। ध्यान दें कि उस परिदृश्य में, SI में संग्रहीत संख्यात्मक मान को कचरा संग्राहक द्वारा बदल दिया गया था, लेकिन इसे संदर्भित किया गया थाइस कदम से पहले और बाद में एक ही वस्तु । यदि इस कदम से पहले यह कार्यक्रम स्टार्टअप के बाद से बनाई गई 23,592 वीं वस्तु को संदर्भित करता है, तो यह आगे भी ऐसा करता रहेगा। दिलचस्प बात यह है कि .NET अधिकांश वस्तुओं के लिए कोई अद्वितीय और अपरिवर्तनीय पहचानकर्ता संग्रहीत नहीं करता है; किसी प्रोग्राम की मेमोरी के दो स्नैपशॉट दिए जाने पर, यह बताना हमेशा संभव नहीं होगा कि पहले स्नैपशॉट में कोई विशेष वस्तु दूसरे में मौजूद है, या यदि इसके लिए सभी निशान छोड़ दिए गए हैं और एक नया ऑब्जेक्ट बनाया गया है जो ऐसा दिखता है सभी अवलोकन योग्य विवरण।


1

person1 और person2 ढेर पर दो अलग-अलग संदर्भ हैं जो ढेर पर एक ही व्यक्ति वस्तु को इंगित करते हैं।

जब आप किसी एक संदर्भ को हटाते हैं, तो इसे स्टैक से हटा दिया जाता है और अब ढेर पर व्यक्ति ऑब्जेक्ट को इंगित नहीं करता है। अन्य संदर्भ अभी भी मौजूद है, ढेर पर मौजूदा व्यक्ति वस्तु की ओर इशारा करता है।

एक बार जब व्यक्ति ऑब्जेक्ट के सभी संदर्भ हटा दिए जाते हैं, तो अंततः कचरा कलेक्टर ऑब्जेक्ट को मेमोरी से हटा देगा।


1

जब आप एक संदर्भ प्रकार बनाते हैं, तो वास्तव में एक ही मेमोरी लोकेशन की ओर इशारा करते हुए सभी ऑब्जेक्ट्स के साथ एक संदर्भ की नकल करते हैं, हालांकि यदि आपने Person2 = Null असाइन किया है, तो इसका कोई प्रभाव नहीं होगा क्योंकि person2 सिर्फ संदर्भ व्यक्ति की कॉपी है और हमने अभी एक कॉपी को मिटा दिया है संदर्भ के


1

ध्यान दें कि आप एक में बदलकर मूल्य शब्दार्थ प्राप्त कर सकते हैं struct

public class Program
{
    static void Main()
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        Person person2 = person1;
        person2.Name = "Shahrooz";
        Console.WriteLine(person1.Name);//Output:Test
        Console.WriteLine(person2.Name);//Output:Shahrooz
        person2 = new Person{Name = "Test2"};
        Console.WriteLine(person2.Name);//Output:Test2

    }
}
public struct Person
{
    public string Name { get; set; }
}

0
public class Program
{
    private static void Main(string[] args)
    {
        var person = new Person {Name = "Test"};
        Console.WriteLine(person.Name);

        Person person2 = person;
        person2.Name = "Shahrooz";
        Console.WriteLine(person.Name);//Output:Shahrooz
        // Here you are just erasing a copy to reference not the object created.
        // Single memory allocation in case of reference type and  parameter
         // are passed as a copy of reference type .   
        person2 = null;
        Console.WriteLine(person.Name);//Output:Shahrooz

    }
}
public class Person
{
    public string Name { get; set; }
}

@doctorlove वास्तव में टिप्पणियों को भूल गया :-(, अच्छी तरह से संदर्भ प्रकार के लिए केवल एक मेमोरी स्पेस आवंटित किया गया है और व्यक्ति 2 या व्यक्ति 1 ... केवल उस संदर्भ प्रकार की प्रतिलिपि है जो स्मृति स्थान की ओर इशारा कर रहे हैं, संदर्भ प्रकार की एक प्रति मिटा रहा है कुछ भी प्रभावित नहीं करेगा।
सूरज सिंह

आपके सुझाव के लिए @doctorlove Thankyou, मैं इसे ध्यान में रखूंगा।
सूरज सिंह

0

आप पहली बार के संदर्भ में नकल person1करने के लिए person2। अभीperson1 और person2उसी वस्तु को देखें, जिसका अर्थ है कि उस वस्तु के मूल्य में संशोधन (अर्थात Nameसंपत्ति को बदलना ) दोनों चर के तहत देखा जा सकता है। फिर, जब आप अशक्त होते हैं, तो आप अपने द्वारा सौंपे गए संदर्भ को हटा रहे हैं person2। इसे अब केवल सौंपा गया person1है। ध्यान दें कि संदर्भ स्वयं नहीं है बदला गया है।

यदि आपके पास एक फ़ंक्शन था जो संदर्भ द्वारा एक तर्क को स्वीकार करता है, तो आप reference to person1 संदर्भ से गुजर सकते हैं , और स्वयं संदर्भ को बदलने में सक्षम होंगे:

public class Program
{
    private static void Main(string[] args)
    {
        var person1 = new Person { Name = "Test" };
        Console.WriteLine(person1.Name);

        PersonNullifier.NullifyPerson(ref person1);
        Console.WriteLine(person1); //Output: null
    }
}


class PersonNullifier
{
    public static void NullifyPerson( ref Person p ) {
        p = null;
    }
}

class  Person {
    public string Name{get;set;}
}

0

कह रही है:

person2.Name = "Shahrooz";

संदर्भ का person2नेतृत्व करने के लिए होता है कि वस्तु के और "परिवर्तन" (परिवर्तन) के संदर्भ का अनुसरण करता है। संदर्भ खुद (का मूल्य )person2 ) अपरिवर्तित है; हम अभी भी उसी "पते" पर उसी उदाहरण का उल्लेख करते हैं।

इसमें निम्नानुसार असाइन करना person2:

person2 = null;

संदर्भ बदलता है । कोई वस्तु परिवर्तित नहीं होती। इस मामले में संदर्भ तीर एक वस्तु से "स्थानांतरित" है, "कुछ भी नहीं" के लिए null। लेकिन जैसा असाइनमेंट person2 = new Person();होता है , वह केवल संदर्भ को बदल देगा। कोई वस्तु उत्परिवर्तित नहीं होती है।

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