ऑब्जेक्ट पास करते समय 'रेफ' कीवर्ड का उपयोग क्यों करें?


290

यदि मैं किसी विधि से कोई ऑब्जेक्ट पास कर रहा हूं, तो मुझे रेफरी कीवर्ड का उपयोग क्यों करना चाहिए? यह डिफ़ॉल्ट व्यवहार वैसे भी नहीं है?

उदाहरण के लिए:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

आउटपुट "बार" है जिसका अर्थ है कि ऑब्जेक्ट को संदर्भ के रूप में पारित किया गया था।

जवाबों:


298

पास करें refयदि आप बदलना चाहते हैं कि वस्तु क्या है:

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

DoSomething कॉल करने के बाद, tमूल का उल्लेख नहीं करता है new TestRef, लेकिन एक पूरी तरह से अलग वस्तु को संदर्भित करता है।

यदि आप किसी अपरिवर्तनीय वस्तु के मूल्य को बदलना चाहते हैं, तो भी यह उपयोगी हो सकता है string। आपके stringद्वारा बनाए गए एक बार के मूल्य को बदल नहीं सकता है । लेकिन एक का उपयोग करके ref, आप एक ऐसा फ़ंक्शन बना सकते हैं जो स्ट्रिंग को दूसरे के लिए बदलता है जिसका एक अलग मूल्य है।

संपादित करें: जैसा कि अन्य लोगों ने उल्लेख किया है। refजब तक जरूरत न हो इसका इस्तेमाल करना अच्छा नहीं है। उपयोग refकरने से कुछ और के लिए तर्क को बदलने की विधि स्वतंत्रता मिलती है, इस संभावना को संभालने के लिए विधि के कॉलर को कोडित करने की आवश्यकता होगी।

इसके अलावा, जब पैरामीटर प्रकार एक ऑब्जेक्ट होता है, तो ऑब्जेक्ट चर हमेशा ऑब्जेक्ट के संदर्भ के रूप में कार्य करते हैं। इसका मतलब यह है कि जब refकीवर्ड का उपयोग किया जाता है तो आपको एक संदर्भ के लिए एक संदर्भ मिला है। यह आपको ऊपर दिए गए उदाहरण में वर्णित चीजों को करने की अनुमति देता है। लेकिन, जब पैरामीटर प्रकार एक आदिम मूल्य (जैसे int) है, तो यदि यह पैरामीटर विधि के भीतर सौंपा गया है, तो तर्क का मान जो पास किया गया था, वह विधि रिटर्न के बाद बदल जाएगा:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}

88

आपको "मान द्वारा एक संदर्भ पासिंग", और "संदर्भ द्वारा एक पैरामीटर / तर्क पास करना" के बीच अंतर करने की आवश्यकता है।

मैंने इस विषय पर एक लंबा लेख लिखा है, हर बार ध्यान से लिखने से बचने के लिए जब भी यह समाचार समूह पर आता है :)


1
वैसे मैंने VB6 को .Net C # कोड में अपग्रेड करते समय समस्या का सामना किया। फ़ंक्शन / विधि हस्ताक्षर हैं जो रेफरी, आउट और सादे पैरामीटर लेते हैं। तो हम एक सादे परम बनाम एक रेफरी के बीच अंतर को बेहतर ढंग से कैसे अलग कर सकते हैं?
bonCodigo

2
@bonCodigo: सुनिश्चित नहीं है कि "बेहतर अंतर" से आपका क्या मतलब है - यह हस्ताक्षर का हिस्सा है, और आपको refकॉल साइट पर भी निर्दिष्ट करना होगा ... आप इसे कहाँ और कहाँ प्रतिष्ठित करना चाहेंगे? शब्दार्थ यथोचित रूप से स्पष्ट होते हैं, लेकिन उन्हें ध्यान से व्यक्त करने की आवश्यकता होती है ("वस्तुओं को संदर्भ द्वारा पारित किया जाता है" जो सामान्य अति-सरलीकरण है)।
जॉन स्कीट

मुझे नहीं पता कि क्यों दृश्य स्टूडियो अभी भी स्पष्ट रूप से नहीं दिखा है कि क्या पारित किया गया है
मॉन्स्टरएमपीजी

3
@MonsterMMORPG: मुझे नहीं पता कि आपको इससे क्या मतलब है, मुझे डर है।
जॉन स्कीट

56

.NET में जब आप किसी पैरामीटर को किसी विधि से पास करते हैं, तो एक कॉपी बनाई जाती है। मूल्य प्रकारों का अर्थ है कि आपके द्वारा मूल्य में किया गया कोई भी संशोधन विधि के दायरे में है, और विधि से बाहर निकलने पर खो जाता है।

संदर्भ प्रकार पास करते समय, एक प्रति भी बनाई जाती है, लेकिन यह एक संदर्भ की एक प्रति है, यानी अब आपके पास उसी ऑब्जेक्ट में मेमोरी में दो संदर्भ हैं। इसलिए, यदि आप ऑब्जेक्ट को संशोधित करने के लिए संदर्भ का उपयोग करते हैं, तो यह संशोधित हो जाता है। लेकिन यदि आप संदर्भ को स्वयं संशोधित करते हैं - तो हमें याद रखना चाहिए कि यह एक प्रति है - फिर विधि से बाहर निकलने पर कोई परिवर्तन भी खो जाता है।

जैसा कि लोगों ने पहले कहा है, एक असाइनमेंट संदर्भ का एक संशोधन है, इस प्रकार खो गया है:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

ऊपर दिए गए तरीके मूल वस्तु को संशोधित नहीं करते हैं।

आपके उदाहरण का थोड़ा संशोधन

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }

6
मुझे यह उत्तर बेहतर लगा तो स्वीकृत उत्तर। यह अधिक स्पष्ट रूप से बताता है कि संदर्भ कीवर्ड के साथ संदर्भ प्रकार चर को पारित करते समय क्या हो रहा है। धन्यवाद!
स्टीफन

17

चूंकि TestRef एक वर्ग है (जो संदर्भ ऑब्जेक्ट हैं), आप इसे रेफरी के रूप में पारित किए बिना टी के अंदर सामग्री को बदल सकते हैं। हालाँकि, यदि आप एक रेफरी के रूप में t पास करते हैं, तो TestRef बदल सकता है कि मूल t क्या दर्शाता है। अर्थात इसे किसी भिन्न वस्तु की ओर इंगित करें।



8

"ऑब्जेक्ट # 24601" फॉर्म के ऑब्जेक्ट पहचानकर्ता के रूप fooमें संदर्भ प्रकारों (जैसे List<T>) के चर के बारे में सोचें । बयान मान लीजिए कारणों पकड़ "ऑब्जेक्ट # 24601" (चार आइटम की सूची) के लिए। फिर कॉलिंग से इसकी लंबाई के लिए ऑब्जेक्ट # 24601 पूछा जाएगा, और यह 4 का जवाब देगा, इसलिए 4 के बराबर होगा।foo = new List<int> {1,5,7,9};foofoo.Lengthfoo.Length

यदि fooकिसी विधि का उपयोग किए बिना पास किया जाता है ref, तो वह विधि ऑब्जेक्ट # 24601 में परिवर्तन कर सकती है। इस तरह के परिवर्तनों के परिणामस्वरूप, foo.Lengthअब समान नहीं हो सकता है 4. विधि, हालांकि, परिवर्तन करने में असमर्थ fooहोगी, जो "ऑब्जेक्ट # 24601" को जारी रखेगा।

fooएक refपैरामीटर के रूप में पास होने से तथाकथित विधि को न केवल ऑब्जेक्ट # 24601 में, बल्कि fooस्वयं को भी परिवर्तन करने की अनुमति मिलेगी । विधि एक नई वस्तु # 8675309 बना सकती है और उसमें एक संदर्भ संग्रहीत कर सकती है foo। यदि ऐसा होता है, तो fooअब "ऑब्जेक्ट # 24601" नहीं होगा, बल्कि "ऑब्जेक्ट # 8675309" होगा।

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


5

यह C. में एक पॉइंटर को पॉइंटर पास करने जैसा है। .NET में यह आपको बदलने की अनुमति देगा कि मूल T क्या संदर्भित करता है, व्यक्तिगत रूप से, हालांकि मुझे लगता है कि अगर आप .NET में ऐसा कर रहे हैं तो आपको शायद एक डिजाइन मुद्दा मिल गया है!


3

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


3

ref mimics (या व्यवहार करता है) एक वैश्विक क्षेत्र के रूप में सिर्फ दो स्कोप के लिए:

  • कोलर
  • कॉल प्राप्त करने वाला।

1

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


4
चाहे आप एक संदर्भ या एक मूल्य प्रकार मान पारित कर रहे हैं, डिफ़ॉल्ट व्यवहार मूल्य से गुजरना है। आपको बस यह समझने की जरूरत है कि संदर्भ प्रकारों के साथ, आप जो मान पास कर रहे हैं वह एक संदर्भ है। यह गुजर के समान नहीं है द्वारा संदर्भ।
जॉन स्कीट

1

Ref निरूपित करता है कि फ़ंक्शन वस्तु पर अपने हाथों को प्राप्त कर सकता है, या केवल उसके मूल्य पर।

संदर्भ से गुजरना किसी भाषा के लिए बाध्य नहीं है; पास-दर-मूल्य, नाम से पास, आवश्यकता से पास आदि के लिए यह एक पैरामीटर बाध्यकारी रणनीति है ...

एक संदर्भ: वर्ग का नाम TestRefइस संदर्भ में एक छिपी हुई बुरी पसंद है;)।

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