रेफरी द्वारा पारित सूची - मुझे इस व्यवहार को समझाने में मदद करें


109

निम्नलिखित कार्यक्रम पर एक नज़र डालें:

class Test
{
    List<int> myList = new List<int>();

    public void TestMethod()
    {
        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList(myList);

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList(List<int> myList)
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

मुझे लगता है कि myListपारित कर दिया जाएगा ref, और उत्पादन होगा

3
4

सूची वास्तव में "रेफरी द्वारा पारित" है, लेकिन केवल sortफ़ंक्शन प्रभावी होता है। निम्नलिखित कथन myList = myList2;का कोई प्रभाव नहीं है।

तो उत्पादन वास्तव में है:

10
50
100

क्या आप मुझे इस व्यवहार की व्याख्या करने में मदद कर सकते हैं? यदि वास्तव myListमें पारित नहीं किया गया है (जैसा कि यह myList = myList2प्रभावी नहीं होने से प्रकट होता है ), तो प्रभाव कैसे होता myList.Sort()है?

मैं यह भी मान रहा था कि प्रभाव को और प्रभावी होने के लिए नहीं:

100
50
10

बस एक अवलोकन (और मैं इस समस्या यहाँ सरलीकृत किया गया है एहसास), लेकिन ऐसा लगता है जैसे कि यह के लिए सबसे अच्छा होगा ChangeListएक वापस जाने के लिए List<int>के बजाय एक हो voidअगर यह एक नई सूची बनाने वास्तव में है।
जेफ बी

जवाबों:


110

आप सूची का संदर्भ दे रहे हैं , लेकिन आप संदर्भ द्वारा सूची चर नहीं दे रहे हैं - इसलिए जब आप चरChangeList के मान को कहते हैं (यानी संदर्भ - "पॉइंटर" सोचते हैं) की प्रतिलिपि बनाई जाती है - और मूल्य में परिवर्तन होता है अंदर पैरामीटर द्वारा ChangeList नहीं देखा जाता है TestMethod

प्रयत्न:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

यह तब स्थानीय-चर myRef (जैसा कि घोषित किया गया है TestMethod) का संदर्भ देता है ; अब, यदि आप अपने अंदर के पैरामीटर को फिर से असाइन करते हैं, तो आप ChangeListचर को फिर से असाइन कर रहे हैं TestMethod


वास्तव में मैं ऐसा कर सकता हूं, लेकिन मैं जानना चाहता हूं कि किस तरह से असर हो रहा है
nmdr

6
@ एनजीएम - जब आप कॉल करते हैं ChangeList, तो केवल संदर्भ की प्रतिलिपि बनाई जाती है - यह एक ही वस्तु है। यदि आप किसी तरह से वस्तु को बदलते हैं, तो उस वस्तु के संदर्भ में सब कुछ परिवर्तन को देखेगा।
मार्क Gravell

225

प्रारंभ में, इसे निम्नानुसार रेखांकन के रूप में दर्शाया जा सकता है:

Init राज्यों

फिर, सॉर्ट लागू किया जाता है myList.Sort(); संग्रह को क्रमबद्ध करें

अंत में, जब आपने किया: myList' = myList2आपने एक संदर्भ खो दिया, लेकिन मूल नहीं और संग्रह क्रमबद्ध रहा।

खोया हुआ संदर्भ

यदि आप संदर्भ ( ref) का उपयोग करते हैं myList'और फिर myListवही (केवल एक संदर्भ) बन जाएगा।

नोट: मैं myList'उस पैरामीटर का प्रतिनिधित्व करने के लिए उपयोग करता हूं जिसे आप उपयोग करते हैं ChangeList(क्योंकि आपने मूल नाम दिया था)


20

यहां इसे समझने का एक आसान तरीका है

  • आपकी सूची ढेर पर बनाई गई एक वस्तु है। चर myListउस वस्तु का एक संदर्भ है।

  • C # में आप कभी भी ऑब्जेक्ट पास नहीं करते हैं, आप उनके संदर्भों को वैल्यू द्वारा पास करते हैं।

  • जब आप सूची ऑब्जेक्ट को पास किए गए संदर्भ ChangeList(उदाहरण के लिए छांटते समय) के माध्यम से एक्सेस करते हैं, तो मूल सूची बदल जाती है।

  • ChangeListविधि पर असाइनमेंट संदर्भ के मूल्य के लिए किया जाता है, इसलिए मूल सूची में कोई बदलाव नहीं किया जाता है (अभी भी ढेर पर लेकिन अब विधि चर पर संदर्भित नहीं है)।


10

यह लिंक आपको C # संदर्भ में पास को समझने में मदद करेगा। मूल रूप से, जब किसी प्रकार के संदर्भ प्रकार के ऑब्जेक्ट को किसी विधि से पास किया जाता है, तो केवल उस ऑब्जेक्ट पर उपलब्ध विधियों को ऑब्जेक्ट की सामग्री को संशोधित कर सकते हैं।

उदाहरण के लिए List.sort () विधि सूची सामग्री को परिवर्तित करती है, लेकिन यदि आप किसी अन्य ऑब्जेक्ट को एक ही चर में असाइन करते हैं, तो यह असाइनमेंट उस विधि के लिए स्थानीय है। यही कारण है कि myList अपरिवर्तित रहता है।

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

(संपादित करें: यह ऊपर दिए गए प्रलेखन का अद्यतन संस्करण है।)


5

C # बस एक उथली प्रतिलिपि बनाता है जब यह मूल्य से गुजरता है जब तक कि प्रश्न में ऑब्जेक्ट निष्पादित नहीं होता है ICloneable(जो स्पष्ट रूप से Listवर्ग नहीं करता है)।

इसका मतलब यह है कि यह Listखुद को कॉपी करता है, लेकिन सूची के अंदर वस्तुओं के संदर्भ समान रहते हैं; यही है, संकेत मूल के रूप में एक ही वस्तुओं को संदर्भित करना जारी रखते हैं List

यदि आप चीजों के मूल्यों को अपने नए Listसंदर्भों में बदलते हैं, तो आप मूल को Listभी बदलते हैं (क्योंकि यह समान वस्तुओं को संदर्भित कर रहा है)। हालाँकि, आप तब क्या myListसंदर्भ पूरी तरह से, एक नए में बदल देते हैं List, और अब केवल मूल Listउन पूर्णांकों को संदर्भित कर रहा है।

अधिक जानकारी के लिए "पासिंग पैरामीटर्स" पर इस MSDN आलेख से पासिंग संदर्भ-प्रकार पैरामीटर अनुभाग पढ़ें ।

StackOverflow से "मैं C # में एक सामान्य सूची कैसे क्लोन करता हूँ" सूची की गहरी प्रतिलिपि बनाने के तरीके के बारे में बात करता है।


3

जबकि मैं इस बात से सहमत हूं कि सभी ने ऊपर कहा है। मेरा इस कोड पर एक अलग लेना है। मूल रूप से आप नई सूची को स्थानीय चर myList को वैश्विक नहीं सौंप रहे हैं। यदि आप ChangeList (सूची myList) के हस्ताक्षर को निजी शून्य ChangeList () में बदलते हैं, तो आप 3, 4 का आउटपुट देखेंगे।

यहां मेरा तर्क है ... भले ही सूची को संदर्भ द्वारा पारित किया गया हो, लेकिन मान लीजिए कि जब आप ChangeList (myList) को कॉल करते हैं, तो आप मान को एक सूचक चर के रूप में पास करते हैं (ग्लोबल) myList। अब यह (स्थानीय) myList चर में संग्रहित है। तो अब आपके (स्थानीय) myList और (वैश्विक) myList एक ही सूची की ओर इशारा कर रहे हैं। अब आप एक प्रकार करते हैं => यह काम करता है क्योंकि (स्थानीय) myList मूल (वैश्विक) myList को संदर्भित करता है अगला आप एक नई सूची बनाते हैं और सूचक को आपके (स्थानीय) myList को असाइन करते हैं। लेकिन जैसे ही फ़ंक्शन बाहर निकलता है (स्थानीय) myList चर नष्ट हो जाता है। HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

2

refकीवर्ड का उपयोग करें ।

गुजरते मापदंडों को समझने के लिए यहां निश्चित संदर्भ देखें ।
विशिष्ट, पर नज़र होना करने के लिए इस कोड के व्यवहार को समझने के।

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

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


0

संदर्भ प्रकार के ऑब्जेक्ट के लिए आवंटित मेमोरी के दो भाग हैं। एक ढेर में और एक ढेर में। स्टैक में हिस्सा (उर्फ एक पॉइंटर) ढेर में उस हिस्से का संदर्भ होता है - जहां वास्तविक मान संग्रहीत होते हैं।

जब रेफ कीवर्ड का उपयोग नहीं किया जाता है, तो स्टैक में भाग की एक प्रति बनाई जाती है और विधि को पारित किया जाता है - हीप में उसी भाग का संदर्भ। इसलिए यदि आप ढेर भाग में कुछ बदलते हैं, तो वे परिवर्तन रुक जाएंगे। यदि आप प्रतिलिपि किए गए पॉइंटर को बदलते हैं - तो इसे ढेर में अन्य स्थान पर संदर्भित करने के लिए असाइन करें - यह विधि के बाहर मूल पॉइंटर को प्रभावित नहीं करेगा।

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