2015-09-07 5 views
7

शायद यह मेरे बारे में थोड़ा सा मूर्ख है, लेकिन मैं वास्तव में "संदर्भ द्वारा पास" के लिए एक सभ्य उपयोग-मामले के बारे में सोच/सोच नहीं सकता। एक अपरिवर्तनीय स्ट्रिंग को बदलना (जैसा कि कुछ अन्य क्यू/जैसा कि उल्लेख किया गया है) आम तौर पर टिकाऊ है और कई चर लौटने पर आमतौर पर टुपल, सूची, सरणी इत्यादि लौटकर बेहतर तरीके से संभाला जाता हैसंदर्भ द्वारा पास सिर्फ एक विरासत है?

example on MSDN मेरी राय में भयानक है; void के रूप में घोषित होने के बजाय, मैं बस Square विधि में एक मान वापस कर रहा हूं।

ऐसा लगता है कि यह सी # के एक विरासत हिस्से का थोड़ा सा हिस्सा है, इसकी एक अभिन्न अंग के बजाय। क्या कोई मुझे समझाने की कोशिश कर सकता है कि यह अभी भी आसपास क्यों है और/या कुछ असली दुनिया के उपयोग-मामले वास्तव में व्यावहारिक हैं (यानी एक अपरिवर्तनीय स्ट्रिंग बदलना लगभग हर मामले में टालने योग्य है)।

+0

क्या होगा यदि आपने एक फ़ंक्शन को परिभाषित किया है जिसे किसी के मान को समायोजित करना है, लेकिन दो, या अधिक (मान प्रकार) चर? – Adimeus

+0

उदाहरण वास्तव में काफी स्पष्ट है। यह बिल्कुल दिखाता है कि कैसे 'रेफरी' काम करता है और इसका उपयोग करने के लिए आप कैसे हैं। हालांकि, मैं ईमानदारी से ऐसी स्थिति के बारे में नहीं सोच सकता जहां एक _has_ किसी और चीज़ पर 'रेफरी' का उपयोग करने के लिए है, इसलिए मैं कुछ जवाब भी देखना चाहूंगा। –

+4

क्या होगा यदि मैं बुलाए गए फ़ंक्शन का उपयोग करके 2 चर अपडेट करना चाहता हूं? रेफरी के रूप में 2 चर को चिह्नित करके इस मामले में रेफरी सहायक हो सकती है। – DarkKnight

उत्तर

6

पी.एस .: मैं @KallDrexx और @newacct से कुछ टिप्पणियों पर पीछा किया। अब मैं देखता हूं कि वे सही थे और मैं गलत था: मेरा जवाब कुछ हद तक भ्रामक था। उत्कृष्ट article "Java is pass-by-value, dammit!" by Scott Stanchfield (जावा-विशिष्ट, लेकिन अभी भी सी # के लिए अधिक प्रासंगिक) अंततः मुझे विश्वास दिलाता है।

मैं अब के लिए के माध्यम से मेरे उत्तर की भ्रामक बिट्स छोड़ दूंगा, लेकिन बाद में उन्हें हटा सकता हूं।


दर्रा संदर्भ द्वारा सिर्फ ref या out मानकों के साथ किया जाता है। सबसे महत्वपूर्ण बात यह है कि सभी संदर्भ प्रकार संदर्भ (इस प्रकार उनका नाम) द्वारा पारित किए जाते हैं, हालांकि यह पारदर्शी रूप से होता है।

  • रोकें बड़े struct रों की नकल जब उनके आसपास गुजर:

    यहाँ तीन लगातार पास-दर-संदर्भ के लिए उपयोग के मामलों रहे हैं। कल्पना करें कि आपके पास byte[] सरणी बाइनरी बड़ी ऑब्जेक्ट (बीएलओबी) का प्रतिनिधित्व करती है, संभवतः कुछ struct प्रकार के मान में कुछ मेगाबाइट्स जिनमें बहुत सारे फ़ील्ड हैं। उस प्रकार का एक मूल्य संभावित रूप से बहुत सारी स्मृति पर कब्जा कर सकता है।अब आप इस मान को कुछ विधि में पास करना चाहते हैं। क्या आप वास्तव में इसे मूल्य से पास करना चाहते हैं, यानी एक अस्थायी प्रतिलिपि बनाएँ?

    आप ref ईरेंस द्वारा उन्हें पास करके बड़े structs की अनावश्यक प्रतिलिपि से बच सकते हैं।

    (सौभाग्य से हमारे लिए, इस तरह के रूप में byte[] सरणियों संदर्भ प्रकार के होते हैं, इसलिए सरणी की सामग्री को पहले से ही refence द्वारा पारित कर रहे हैं।)

    यह अक्सर सुझाव दिया है (Microsoft's Framework Design Guidelines में जैसे) है कि प्रकार value- होने टाइप अर्थशास्त्र को संदर्भ प्रकार के रूप में कार्यान्वित किया जाना चाहिए यदि वे एक निश्चित आकार (32 बाइट्स) से अधिक हो, तो यह उपयोग केस बहुत बार नहीं होना चाहिए।

  • उत्परिवर्तन। यदि आप struct मान को पारित करने के लिए एक विधि चाहते हैं, और आप चाहते हैं कि कॉलर उस ऑब्जेक्ट के अपने संस्करण के उत्परिवर्तन का निरीक्षण करे, तो आपको संदर्भ (ref) से गुजरना होगा। यदि मान मूल्य के अनुसार विधि को पास किया जाता है, तो उसे एक प्रति प्राप्त होती है; कॉपी को म्यूट करने से मूल ऑब्जेक्ट को अनमोडिफाइड छोड़ दिया जाएगा।

    इस बिंदु का ऊपर से जुड़े फ्रेमवर्क डिजाइन दिशानिर्देश आलेख में भी उल्लेख किया गया है।

    म्यूटेबल वैल्यू प्रकारों के विरुद्ध व्यापक अनुशंसा नोट करें (उदा। "Why are mutable structs evil?" देखें)। आपको मूल्यों के साथ शायद ही कभी ref या out पैरामीटर का उपयोग करना होगा।

  • COM इंटरॉपthis answer में उल्लेख किया है, जैसा कि अक्सर ref और out मानकों की घोषणा करने की आवश्यकता है।

+0

बीएलओबी हैंडलिंग समझ में आता है। यह कुछ ऐसा नहीं है जो मैंने पार किया है, इसलिए मैंने इसके बारे में नहीं सोचा। धन्यवाद! – Trent

+0

@FizzBuzz: लेकिन निश्चित रूप से आप एक बिंदु पर हैं या दूसरे ने एक विधि को एक स्ट्रिंग पास कर दी है जो लंबाई में केवल कुछ वर्णों से अधिक है। यदि वह मूल्य से पारित किया गया था, तो आपको हमेशा पूरे चरित्र बफर के आसपास प्रतिलिपि बनाना होगा। सौभाग्य से, चूंकि 'स्ट्रिंग' और 'char []' संदर्भ प्रकार हैं, ऐसा नहीं होगा। लेकिन अभी भी * दृश्यों के पीछे * पास-दर-संदर्भ चल रहा है। – stakx

+0

हाँ, मुझे लगता है कि संदर्भ प्रकार एक विशेष परिदृश्य का थोड़ा सा है। यह सी # में दृश्यों के पीछे है। मुझे इस बात में अधिक दिलचस्पी है कि जब * इंटरफ़ेस उदाहरण जैसे 'रेफ' निर्दिष्ट करने के लिए * होगा। – Trent

4

आप एक समारोह है कि एक मूल्य mutates (सूचना, मूल्य, नहीं वस्तु) करना चाहते हैं मान लीजिए, और आप इस समारोह में कुछ सफलता सूचक वापस जाने के लिए चाहते हैं। एक अच्छा अभ्यास सफलता/विफलता का संकेत देने वाले बुलियन को वापस करना होगा, लेकिन मूल्य के बारे में क्या? तो अगर आप का प्रयोग कर एक ref:

bool Mutate(ref int val) 
{ 
    if(val > 0) 
    { 
    val = val * 2; 
    return true; 
    } 
    return false; 
} 
+0

क्या आप 'TryX' विधियों का उपयोग कर शेष कोड के साथ इस मामले में 'आउट' पैरामीटर का उपयोग नहीं करेंगे? (बेशक, आप इस तरह से उपयोग कर सकते हैं ..) – Sayse

+1

@Sayse - मुझे नहीं लगता कि यह बेहतर क्यों है। अनचाहे अपवाद जोड़ना, और भ्रमित एपीआई उत्पन्न करना। 'रेफरी' स्पष्ट है, यह एक वैध मान की अपेक्षा करता है, 'आउट' एक अनियमित चर स्वीकार कर सकता है। – Amit

+0

क्योंकि आपका शेष कोड शायद इसका उपयोग कर रहा है ('int.TryParse' et al।) तो मैं स्थिरता का पक्ष लेगा। मैं आपके उत्तर – Sayse

3

यह सच है कि सी # में वहां आम तौर पर ref और out के लिए विकल्प हैं - अगर आप फोन करने वाले के लिए एक से अधिक मूल्य वापस करना चाहते उदाहरण के लिए, यदि आप एक टपल लौट सकता है, या एक कस्टम प्रकार, या पैरामीटर के रूप में एक संदर्भ प्रकार प्राप्त करें और इसके अंदर कई मानों को बदलें।

हालांकि, अब भी इन खोजशब्दों इंटरॉप तरह की परिस्थितियों में एक सुविधाजनक समाधान हो सकता है:

// C 
int DoSomething(int input, int *output); 

// C# 
[DllImport(...)] 
static extern int DoSomething(int input, ref int output); 
+0

'रेफरी' और 'आउट' के साथ असहमत नहीं हूं, केवल उन परिदृश्य नहीं हैं जहां संदर्भ संदर्भित होते हैं। सबसे महत्वपूर्ण, सभी संदर्भ प्रकार संदर्भ द्वारा पारित किए जाते हैं (यही कारण है कि उन्हें संदर्भ प्रकार कहा जाता है), इससे कोई फर्क नहीं पड़ता कि वे किसी विधि के हस्ताक्षर में कहां दिखाई देते हैं। – stakx

+0

@stakx: संदर्भ प्रकार प्राप्त होते हैं * उनका संदर्भ ** मान ** द्वारा पारित किया जाता है। * यही कारण है कि आप कॉलर नोटिस के बिना अपने मानकों को नए मानों को स्वतंत्र रूप से असाइन कर सकते हैं - क्योंकि यह पारित चर के लिए उपनाम नहीं है। – Joey

+0

@stakx: दरअसल, जब आप 'रेफरी' कीवर्ड के बिना संदर्भ प्रकार पास करते हैं, तो आप इसे संदर्भ से पास नहीं कर रहे हैं, लेकिन आप इसे मूल्य के संदर्भ में पारित कर रहे हैं। –

0

वास्तव में केवल कुछ मामले हैं जहां स्पष्ट रेफ पैरामीटर एक कार्यात्मक बिंदु से उपयोगी होते हैं।

एक उदाहरण मैंने देखा है:

public static void Swap<T>(ref T a, ref T b) 
{ 
    var temp = a; 
    a = b; 
    b = temp; 
} 

इस तरह की एक विधि उदाहरण के लिए, किसी प्रकार का एल्गोरिदम में उपयोगी है।

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

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

असल में, संदर्भ द्वारा एक संरचना को पार करना वास्तव में संरचना के डेटा में सूचक को पास करने का एक सुरक्षित तरीका है, जो बड़े structs के लिए एक सामान्य निम्न स्तर अनुकूलन तकनीक है।

एक ऐसी सुविधा है जो कम स्तर के अनुकूलन को विरासत सुविधा प्रदान करती है? यह आपके दृष्टिकोण पर निर्भर हो सकता है, लेकिन आप केवल सी # उपयोगकर्ता नहीं हैं। उनमें से कई आपको बताएंगे कि पॉइंटर्स और अन्य निम्न स्तर की संरचनाओं की उपलब्धता कुछ अन्य भाषाओं में सी # के बड़े फायदों में से एक है। मैं एक के लिए नहीं सोचता कि रेफ पैरामीटर एक विरासत विशेषता है, वे कुछ परिदृश्यों में उपयोगी भाषा का एक अभिन्न हिस्सा हैं।

इसका मतलब यह नहीं है कि को रेफ पैरामीटर का उपयोग करना है, जैसे कि आपको LINQ, async/await या किसी अन्य भाषा सुविधा का उपयोग करने की आवश्यकता नहीं है। बस खुश रहें जब आपको इसकी आवश्यकता हो।

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