C # में संदर्भ-प्रकार चर के लिए "रेफ" का उपयोग क्या है?


176

मैं समझता हूँ कि अगर मैं एक मूल्य-प्रकार (पारित int, structआदि) एक पैरामीटर के रूप (बिना refकीवर्ड), कि चर की एक प्रति विधि को पारित कर दिया है, लेकिन अगर मैं का उपयोग refकीवर्ड कि चर के लिए एक संदर्भ पारित हो जाता है, नया नहीं है।

लेकिन संदर्भ-प्रकारों के साथ, कक्षाओं के बिना, यहां तक ​​कि refकीवर्ड के बिना , एक संदर्भ विधि के लिए पारित किया जाता है, एक प्रति नहीं। तो refसंदर्भ-प्रकार के साथ कीवर्ड का उपयोग क्या है ?


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

var x = new Foo();

निम्नलिखित में क्या अंतर है?

void Bar(Foo y) {
    y.Name = "2";
}

तथा

void Bar(ref Foo y) {
    y.Name = "2";
}

जवाबों:


154

आप किन fooबिंदुओं का उपयोग करके बदल सकते हैं y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"

17
इसलिए आपको मूल रूप से मूल संदर्भ का संदर्भ मिलता है
lhahne

2
आप मूल संदर्भ 'संदर्भित' को बदल सकते हैं, इसलिए हां।
user7116

1
क्रिस, आपकी व्याख्या महान है; इस अवधारणा को समझने में मेरी मदद करने के लिए धन्यवाद।
एंड्रियास ग्रेच

4
तो किसी वस्तु पर 'Ref' का उपयोग करना C ++ में डबल पॉइंटर्स का उपयोग करने जैसा है?
टॉम हेज़ल

1
@TomHazel: -ish , बशर्ते कि आप C ++ में "डबल" पॉइंटर्स का उपयोग कर रहे हैं ताकि पॉइंटर पॉइंट्स को बदल सकें ।
user7116

29

ऐसे मामले हैं जहां आप वास्तविक संदर्भ को संशोधित करना चाहते हैं न कि वस्तु को इंगित करना:

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);

21

जॉन स्कीट ने C # में पैरामीटर पास करने के बारे में एक शानदार लेख लिखा । यह स्पष्ट रूप से सटीक व्यवहार और मूल्य से गुजरने वाले मापदंडों का उपयोग करता है, संदर्भ ( ref), और आउटपुट द्वारा ( out)।

refमापदंडों के संबंध में उस पृष्ठ का एक महत्वपूर्ण उद्धरण यहां दिया गया है:

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


11
मुझे एक संदर्भ-मूल्य पास करने के लिए अपने कुत्तों को पट्टा देने के लिए दोस्त की उपमा पसंद है ... हालांकि यह जल्दी से टूट जाता है, क्योंकि मुझे लगता है कि आप शायद यह नोटिस करेंगे कि आपके दोस्त ने आपके डबर्मन के लिए आपका shitzu अप करने से पहले आपको वापस सौंप दिया। पट्टा ;-)
corlettk

16

बहुत अच्छी तरह से यहाँ समझाया गया है: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

लेख से सार:

एक संदर्भ प्रकार के एक चर में इसका डेटा सीधे नहीं होता है; इसमें इसके डेटा का संदर्भ है। जब आप किसी संदर्भ-प्रकार के पैरामीटर को मान से पास करते हैं, तो संदर्भ द्वारा इंगित किए गए डेटा को बदलना संभव है, जैसे कि वर्ग के सदस्य का मान। हालाँकि, आप संदर्भ का मान नहीं बदल सकते हैं; यही है, आप एक ही संदर्भ का उपयोग एक नए वर्ग के लिए मेमोरी आवंटित करने के लिए नहीं कर सकते हैं और यह ब्लॉक के बाहर बनी रहती है। ऐसा करने के लिए, रेफरी या आउट कीवर्ड का उपयोग करके पैरामीटर पास करें।


4
स्पष्टीकरण वास्तव में बहुत अच्छा है। हालाँकि, SO पर लिंक-केवल उत्तर हतोत्साहित किए जाते हैं। मैंने लेख से एक सारांश जोड़ा, यहाँ पाठकों के लिए एक सुविधा के रूप में।
मार्सेल

10

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

C # में 'आउट' कीवर्ड भी होता है जो कि रेफ की तरह बहुत कुछ होता है, सिवाय इसके कि 'रेफ' के साथ, मेथड को कॉल करने से पहले आरंभीकृत किया जाना चाहिए, और 'आउट' के साथ आपको रिसीविंग मेथड में वैल्यू असाइन करना होगा।


5

यह आपको दिए गए संदर्भ को संशोधित करने की अनुमति देता है

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

यदि आप इसमें दिए गए संदर्भ की परवाह नहीं करते हैं, तो आप इसका उपयोग भी कर सकते हैं :

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

4

कोड का एक और गुच्छा

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}

3

मौजूदा उत्तरों के अलावा:

जैसा कि आपने 2 विधियों के अंतर के लिए कहा: उपयोग refया करते समय कोई सह (ntra) भिन्नता नहीं है out:

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}

1

एक विधि में एक पैरामीटर हमेशा एक प्रति पारित करने के लिए लगता है, सवाल क्या है की एक प्रति है। एक कॉपी कंस्ट्रक्टर द्वारा एक ऑब्जेक्ट के लिए किया जाता है और चूंकि सभी चर C # में ऑब्जेक्ट होते हैं, मेरा मानना ​​है कि यह उन सभी के लिए मामला है। चर (वस्तुएं) कुछ पतों पर रहने वाले लोगों की तरह हैं। हम या तो उन पतों पर रहने वाले लोगों को बदलते हैं या हम उन पतों पर रहने वाले लोगों के लिए फोन बुक (उथली प्रतियाँ) बनाते हैं। इसलिए, एक से अधिक पहचानकर्ता एक ही पते का उल्लेख कर सकते हैं। संदर्भ प्रकारों में अधिक स्थान की इच्छा होती है, इसलिए मूल्य प्रकारों के विपरीत जो एक तीर द्वारा सीधे स्टैक में अपने पहचानकर्ता से जुड़े होते हैं, उनके पास हीप में एक और पते के लिए मूल्य होता है (रहने के लिए एक बड़ा स्थान)। यह स्थान ढेर से लिया जाना चाहिए।

मूल्य प्रकार: इंडेंटिफायर (जिसमें स्टैक मूल्य का मान = पता होता है) ----> मूल्य प्रकार का मूल्य

संदर्भ प्रकार: पहचानकर्ता (जिसमें स्टैक मान का मान = पता होता है) ----> (जिसमें मूल्य = हीप मूल्य का पता होता है) ----> हीप मूल्य (अधिकतर अक्सर अन्य मानों के पते होते हैं), अलग-अलग चिपके हुए तीर की कल्पना करते हैं ऐरे को निर्देश [0], ऐरे [1], ऐरे [2]

मान बदलने का एकमात्र तरीका तीरों का पालन करना है। यदि एक तीर खो जाता है / बदल जाता है तो मान अनुपलब्ध है।


-1

संदर्भ चर एक स्थान से दूसरे स्थान पर पते को ले जाते हैं, इसलिए किसी भी स्थान पर उन पर किसी भी अपडेशन को सभी स्थानों पर प्रतिबिंबित किया जाएगा जो कि आरईएफ का उपयोग होता है। संदर्भ चर (405) तब तक अच्छे हैं जब तक कि विधि में पारित संदर्भ चर को कोई नई मेमोरी आवंटित नहीं की जाती है।

एक बार जब नई मेमोरी आवंटित होती है (410) तो इस ऑब्जेक्ट (408) पर मूल्य परिवर्तन हर जगह प्रतिबिंबित नहीं होगा। इसके लिए रेफ आता है। रेफ रेफरेंस का संदर्भ है, इसलिए जब भी नई मेमोरी आवंटित की जाती है, तो उसे पता चल जाता है क्योंकि यह उस स्थान की ओर इशारा करता है इसलिए मान को किसी ओनने द्वारा साझा किया जा सकता है। आप अधिक स्पष्टता के लिए छवि देख सकते हैं।

संदर्भ चर में संदर्भ

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