2010-06-05 8 views
24

मैं समझने की कोशिश कर रहा हूं कि सी # में कक्षा क्षेत्र में "संदर्भ" कैसे असाइन किया जाए।मैं सी # में कक्षा के क्षेत्र में "संदर्भ" द्वारा कैसे निर्दिष्ट करूं?

public class X 
{ 

    public X() 
    { 

    string example = "X"; 

    new Y(ref example); 

    new Z(ref example); 

    System.Diagnostics.Debug.WriteLine(example); 

    } 

} 

public class Y 
{ 

    public Y(ref string example) 
    { 
    example += " (Updated By Y)"; 
    } 

} 

public class Z 
{ 

    private string _Example; 

    public Z(ref string example) 
    { 

    this._Example = example; 

    this._Example += " (Updated By Z)"; 

    } 

} 

var x = new X(); 

ऊपर कोड चलाते समय उत्पादन होता है:

एक्स (करके वाई अपडेट किया गया)

और न:

मैं विचार करने के लिए निम्न उदाहरण है

एक्स (द्वारा अपडेट किया गया वाई) (जेड द्वारा अपडेट किया गया)

जैसा कि मैंने आशा की थी।

ऐसा लगता है कि किसी फ़ील्ड में "रेफ पैरामीटर" असाइन करना संदर्भ खो देता है।

क्या किसी क्षेत्र को निर्दिष्ट करते समय संदर्भ को पकड़ने का कोई तरीका है?

धन्यवाद।

उत्तर

8

नहीं रेफरी पूरी तरह से एक कॉलिंग सम्मेलन है। आप किसी क्षेत्र को अर्हता प्राप्त करने के लिए इसका उपयोग नहीं कर सकते हैं। ज़ेड में, _Example पारित स्ट्रिंग संदर्भ के मान पर सेट हो जाता है। फिर आप + = का उपयोग कर इसके लिए एक नया स्ट्रिंग संदर्भ असाइन करते हैं। आप उदाहरण के लिए कभी असाइन नहीं करते हैं, इसलिए रेफरी का कोई प्रभाव नहीं पड़ता है।

एकमात्र कार्य-आसपास जो आप चाहते हैं उसके लिए एक साझा म्यूटेबल रैपर ऑब्जेक्ट (एक सरणी या एक काल्पनिक स्ट्रिंगवॉपर) है जिसमें संदर्भ (यहां एक स्ट्रिंग) है। आम तौर पर, यदि आपको इसकी आवश्यकता है, तो आप कक्षाओं को साझा करने के लिए एक बड़ी म्यूटेबल ऑब्जेक्ट पा सकते हैं।

public class Z { 
    private string _Example; 

    public Z(ref string example) { 
     example = this._Example += " (Updated By Z)"; 
    } 
} 

आउटपुट::

public class StringWrapper 
{ 
    public string s; 
    public StringWrapper(string s) 
    { 
    this.s = s; 
    } 

    public string ToString() 
    { 
    return s; 
    } 
} 

public class X 
{ 
    public X() 
    { 
    StringWrapper example = new StringWrapper("X"); 
    new Z(example) 
    System.Diagnostics.Debug.WriteLine(example); 
    } 
} 

public class Z 
{ 
    private StringWrapper _Example; 
    public Z(StringWrapper example) 
    { 
    this._Example = example; 
    this._Example.s += " (Updated By Z)"; 
    } 
} 
+0

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

+0

मैंने एक उदाहरण दिया है जिसे स्पष्ट करना चाहिए। जैसा कि मैंने कहा, एक असली डिजाइन में स्ट्रिंगवॉपर शायद एक स्ट्रिंग से अधिक होल्डिंग ऑब्जेक्ट होगा। –

+0

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

3

आप जेड क्लास में संदर्भ अद्यतन करने के लिए भूल गया एक्स (करके वाई नवीनीकृत) (तक जेड अद्यतन किया गया)

प्वाइंट ध्यान में रखना है कि है स्ट्रिंग के लिए + = ऑपरेटर String.Concat() विधि को कॉल करता है। जो नया स्ट्रिंग ऑब्जेक्ट बनाता है, यह स्ट्रिंग के मान को अपडेट नहीं करता है। स्ट्रिंग ऑब्जेक्ट्स अपरिवर्तनीय हैं, स्ट्रिंग क्लास में कोई तरीका या फ़ील्ड नहीं है जो आपको मान बदलने देता है। एक नियमित संदर्भ प्रकार के डिफ़ॉल्ट व्यवहार से बहुत अलग है।

तो यदि आप स्ट्रिंग विधि या ऑपरेटर का उपयोग करते हैं, तो आपको हमेशा एक वैरिएबल पर वापसी मान असाइन करना होगा। यह सुंदर प्राकृतिक वाक्यविन्यास है, मूल्य प्रकार एक ही तरीके से व्यवहार करते हैं। यदि आप स्ट्रिंग के बजाय int का उपयोग करते हैं तो आपका कोड बहुत समान होगा।

+1

विचार हंस के लिए धन्यवाद। मेरा उदाहरण गलती पर है - इसे सरल रखने के लिए मैंने कन्स्ट्रक्टर में अपडेट किया है। हकीकत में क्षेत्र को कन्स्ट्रक्टर में सेट किया जाएगा लेकिन ऑब्जेक्ट को तत्काल करने के बाद अपडेट किसी अन्य विधि में होता है। – Jamie

56

जैसा कि अन्य ने ध्यान दिया है, आपके पास "ref to variable" प्रकार का क्षेत्र नहीं हो सकता है। हालांकि, सिर्फ यह जानकर कि आप ऐसा नहीं कर सकते हैं शायद असंतुष्ट है; आप शायद पहले भी जानना चाहते हैं, क्यों नहीं, और दूसरा, इस प्रतिबंध को कैसे प्राप्त करें।

कारण है कि केवल तीन संभावनाएं देखते हैं क्योंकि:

1) रेफरी प्रकार के क्षेत्रों

2) रेफरी प्रकार के असुरक्षित क्षेत्रों

3) अस्थायी भंडारण का उपयोग न करें की अनुमति दें को अनुमति न दें स्थानीय चर के लिए पूल (उर्फ "स्टैक")

मान लीजिए कि हमने रेफ प्रकार के फ़ील्ड की अनुमति दी है। तो फिर तुम

public ref int x; 
void M() 
{ 
    int y = 123; 
    this.x = ref y; 
} 

कर सकता है और अब y M पूर्ण होने के बाद पहुँचा जा सकता है। इसका मतलब है कि या तो हम मामले में हैं (2) - this.x तक पहुंचने से दुर्घटनाग्रस्त हो जाएगा और मर जाएगा क्योंकि वाई के लिए भंडारण अब मौजूद नहीं है - या हम मामले में हैं (3), और स्थानीय y कचरे पर संग्रहीत है एकत्रित ढेर, अस्थायी स्मृति पूल नहीं।

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

ध्यान दें कि अज्ञात कार्यों के बंद-ओवर चर के स्थानीय चर के लिए हम विकल्प (3) चुनते हैं; उन स्थानीय चर को अस्थायी पूल से आवंटित नहीं किया जाता है।

जो हमें दूसरे प्रश्न पर लाता है: आप इसके आसपास कैसे जाते हैं? कारण है कि आप एक रेफरी क्षेत्र चाहते हैं एक गेटर और अन्य चर के सेटर बनाने के लिए है, तो यह है कि पूरी तरह से कानूनी है:

sealed class Ref<T> 
{ 
    private readonly Func<T> getter; 
    private readonly Action<T> setter; 
    public Ref(Func<T> getter, Action<T> setter) 
    { 
     this.getter = getter; 
     this.setter = setter; 
    } 
    public T Value { get { return getter(); } set { setter(value); } } 
} 
... 
Ref<int> x; 
void M() 
{ 
    int y = 123; 
    x = new Ref<int>(()=>y, z=>{y=z;}); 
    x.Value = 456; 
    Console.WriteLine(y); // 456 -- setting x.Value changes y. 
} 

और वहाँ तुम जाओ। y जीसी ढेर पर संग्रहीत है, और x एक ऐसी वस्तु है जिसमें y प्राप्त करने और सेट करने की क्षमता है।

ध्यान दें कि सीएलआर रेफ स्थानीय लोगों और रिफ्रेंसिंग विधियों का समर्थन करता है, हालांकि सी # नहीं है। शायद सी # का एक काल्पनिक भविष्य संस्करण इन सुविधाओं का समर्थन करेगा; मैंने इसे प्रोटोटाइप किया है और यह अच्छी तरह से काम करता है। हालांकि, यह प्राथमिकता सूची पर वास्तविक नहीं है, इसलिए मुझे अपनी आशाएं नहीं मिलेंगी।

अद्यतन: ऊपर दिए गए अनुच्छेद में उल्लिखित सुविधा अंततः सी # 7 में वास्तविक के लिए लागू की गई थी। हालांकि, आप अभी भी एक क्षेत्र में एक रेफरी स्टोर नहीं कर सकते हैं।

+0

गेटर और सेटर की आवश्यकता क्यों है - क्या बॉक्स को बने रहने के लिए महत्वपूर्ण बंद है?क्या रेफ {सार्वजनिक टी वैल्यू {get; सेट; } } बजाय? –

+1

@ChrisMoschini: निश्चित रूप से, यह पूरी तरह से संभव होगा। लेकिन फिर आप * एक मान * के संदर्भ में गुजर रहे हैं, न कि * एक चर *। प्रश्न का पूरा बिंदु यह है कि "मैं एक चर के संदर्भ को कैसे संग्रहीत करूं?" मान लें कि स्थानीय चर वाई के बजाय, आप एक सरणी के 12 वें तत्व को "संदर्भ" संग्रहित करना चाहते थे, ताकि संदर्भ को उत्परिवर्तित करने से सरणी के 12 वें तत्व को बदल दिया जा सके। आपका प्रस्तावित प्रकार उस परिदृश्य में मदद नहीं करेगा। –

+0

सच है, हालांकि न तो गुज़र जाएगा। कुछ मोड (रेफ सरणी [12]), जो कि सवाल की भावना के लिए अधिक प्रतीत होता है। हमारे प्रस्तावों में से कोई भी आपको एक वैल्यू प्रकार नहीं देता है जो कि बॉक्स किया गया है और दुर्भाग्य से बॉक्सिंग बनी हुई है। Nullable वास्तव में पूछताछ करने वाला प्रयास करने वाला सबसे नज़दीकी मैच हो सकता है। –

संबंधित मुद्दे