C का उपयोग कब करें और कब C # में आवश्यक नहीं है


104

मेरे पास एक ऑब्जेक्ट है जो प्रोग्राम की मेमोरी स्थिति में है और मेरे पास कुछ अन्य कार्यकर्ता फ़ंक्शन भी हैं जो मैं राज्य को संशोधित करने के लिए ऑब्जेक्ट पास करता हूं। मैं कार्यकर्ता कार्यों के लिए इसे पारित कर रहा हूँ। हालांकि मैं निम्नलिखित समारोह में आया था।

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

यह मुझे confuses क्योंकि दोनों received_sऔर remoteEPसमारोह से लौट रहे हैं सामान। क्यों remoteEPएक की जरूरत है refऔर received_sनहीं करता है?

मैं एसी प्रोग्रामर भी हूं इसलिए मुझे अपने सिर से पॉइंटर्स निकलने में समस्या हो रही है।

संपादित करें: ऐसा लगता है कि C # में ऑब्जेक्ट हूड के नीचे ऑब्जेक्ट के लिए पॉइंटर्स हैं। इसलिए जब आप किसी फ़ंक्शन को ऑब्जेक्ट पास करते हैं तो आप पॉइंटर के माध्यम से ऑब्जेक्ट कॉन्टेंट को संशोधित कर सकते हैं और फ़ंक्शन के लिए पास की गई एकमात्र चीज ऑब्जेक्ट के लिए पॉइंटर है इसलिए ऑब्जेक्ट को खुद कॉपी नहीं किया जा रहा है। आप रेफरी या बाहर का उपयोग करते हैं यदि आप फ़ंक्शन को स्विच करना या फ़ंक्शन में एक नई ऑब्जेक्ट बनाना चाहते हैं जो डबल पॉइंटर की तरह है।

जवाबों:


216

संक्षिप्त उत्तर: तर्क पारित करने पर मेरा लेख पढ़ें ।

दीर्घ उत्तर: जब संदर्भ प्रकार का पैरामीटर मान द्वारा पारित किया जाता है, तो केवल संदर्भ पारित किया जाता है, वस्तु की प्रतिलिपि नहीं । यह C या C ++ में पॉइंटर (मान द्वारा) पास करने जैसा है। पैरामीटर के मूल्य में परिवर्तन को कॉलर द्वारा नहीं देखा जाएगा, लेकिन ऑब्जेक्ट में परिवर्तन जिसे संदर्भ बिंदुओं को देखा जाएगा

जब एक पैरामीटर (किसी भी प्रकार का) संदर्भ द्वारा पारित किया जाता है , तो इसका मतलब है कि पैरामीटर में कोई भी परिवर्तन कॉलर द्वारा देखा जाता है - पैरामीटर में परिवर्तन परिवर्तनशील हैं

लेख इस सब को और अधिक विस्तार से बताता है, निश्चित रूप से :)

उपयोगी उत्तर: आपको लगभग कभी भी रेफ / आउट का उपयोग करने की आवश्यकता नहीं है । यह मूल रूप से एक और वापसी मूल्य प्राप्त करने का एक तरीका है, और आमतौर पर इसे सटीक रूप से बचा जाना चाहिए क्योंकि इसका मतलब है कि विधि शायद बहुत अधिक करने की कोशिश कर रही है। हमेशा ऐसा नहीं होता है ( TryParseआदि उचित उपयोग के विहित उदाहरण हैं out) लेकिन रेफ / आउट का उपयोग एक सापेक्ष दुर्लभता होना चाहिए।


38
मुझे लगता है कि आपको अपना संक्षिप्त उत्तर और लंबा उत्तर मिला हुआ है; यह एक बड़ा लेख है!
आउटलाव प्रोग्रामर

23
@ ओटलॉ: हां, लेकिन संक्षिप्त जवाब ही, लेख को पढ़ने का निर्देश, केवल 6 शब्द लंबा है :)
जॉन स्कीट

27
संदर्भ! :)
गोनज़ोब्रिन्स

5
@ लियम आपके जैसे रेफरी का उपयोग आपके लिए अधिक स्पष्ट हो सकता है, लेकिन यह वास्तव में अन्य प्रोग्रामर (जो लोग जानते हैं कि कीवर्ड वैसे भी क्या करता है) को भ्रमित कर सकता है, क्योंकि आप अनिवार्य रूप से संभावित कॉलर्स को बता रहे हैं, "मैं आपको चर को संशोधित कर सकता हूं कॉलिंग विधि में उपयोग किया जाता है, अर्थात इसे किसी भिन्न ऑब्जेक्ट पर पुनः असाइन करें (या यदि संभव हो तो शून्य करने के लिए भी), इसलिए इसे लटकाएँ नहीं, या सुनिश्चित करें कि जब आपने इसे इसके साथ किया है, तो आप इसे सत्यापित करें। यह बहुत मजबूत है, और "इस ऑब्जेक्ट को संशोधित किया जा सकता है" से पूरी तरह से अलग है, जो हमेशा ऐसा होता है जब भी आप एक पैरामीटर के रूप में ऑब्जेक्ट संदर्भ को पास करते हैं।
मबगिल

1
@ M.Mimpen: C # 7 विल (उम्मीद) की अनुमति हैif (int.TryParse(text, out int value)) { ... use value here ... }
जॉन स्कीट

26

एक गैर-रेफरी पैरामीटर को एक पॉइंटर होने के रूप में सोचो, और एक डबल-पॉइंटर के रूप में एक रेफरी पैरामीटर। इससे मुझे सबसे ज्यादा मदद मिली।

आपको लगभग कभी भी रेफ द्वारा मान पास नहीं करना चाहिए। मुझे संदेह है कि यदि यह इंटरॉप सरोकारों के लिए नहीं था, तो .नेट टीम ने इसे मूल विनिर्देश में शामिल नहीं किया। मापदंडों को हल करने वाली अधिकांश समस्या से निपटने का OO तरीका निम्न है:

कई वापसी मूल्यों के लिए

  • कई वापसी मूल्यों का प्रतिनिधित्व करने वाली संरचनाएँ बनाएँ

विधि कॉल के परिणाम के रूप में एक विधि में परिवर्तन होने वाली आदिमता के लिए (विधि में आदिम मापदंडों पर दुष्प्रभाव हैं)

  • ऑब्जेक्ट को आवृत्ति विधि के रूप में लागू करें और विधि कॉल के भाग के रूप में ऑब्जेक्ट की स्थिति (पैरामीटर नहीं) में हेरफेर करें
  • मल्टीपल रिटर्न वैल्यू सॉल्यूशन का उपयोग करें और रिटर्न वैल्यू को अपने राज्य में मर्ज करें
  • एक ऐसी वस्तु बनाएं जिसमें ऐसी अवस्था हो जिसमें एक विधि द्वारा हेरफेर किया जा सकता है और उस वस्तु को पैरामीटर के रूप में पास किया जा सकता है, न कि स्वयं प्रिमिटिव को।

8
अच्छे भगवान। मुझे यह समझने के लिए यह 20x पढ़ना होगा। मेरे लिए अतिरिक्त काम का एक गुच्छा जैसा लगता है बस कुछ सरल करने के लिए।
पॉजिटिव

.NET फ्रेमवर्क आपके # 1 नियम का पालन नहीं करता है। ('कई वापसी मूल्यों के लिए संरचनाएं बनाएँ')। उदाहरण के लिए लें IPAddress.TryParse(string, out IPAddress)
Swen Kooij

@SwenKooij तुम सही हो। मैं यह मान रहा हूं कि अधिकांश स्थानों पर जहां वे मापदंडों का उपयोग करते हैं (ए) वे एक Win32 एपीआई लपेट रहे हैं, या (बी) यह शुरुआती दिन थे, और सी ++ प्रोग्रामर निर्णय ले रहे थे।
माइकल मीडोज

@SwenKooij मुझे पता है कि यह जवाब कई साल देर से है, लेकिन ऐसा होता है। हम TryParse के लिए उपयोग किए जाते हैं, लेकिन इसका मतलब यह नहीं है कि यह अच्छा है। यह बेहतर होता अगर if (int.TryParse("123", out var theInt) { /* use theInt */ }हमारे पास होने के बजाय var candidate = int.TrialParse("123"); if (candidate.Parsed) { /* do something with candidate.Value */ }यह अधिक कोड होता, लेकिन C # भाषा डिजाइन के साथ बहुत अधिक सुसंगत है।
माइकल

9

आप शायद एक संपूर्ण C # ऐप लिख सकते हैं और रेफ द्वारा कभी कोई ऑब्जेक्ट / स्ट्रक्चर्स पास नहीं कर सकते।

मेरे पास एक प्रोफेसर थे जिन्होंने मुझे यह बताया:

एकमात्र स्थान जिसका आप उपयोग करते हैं, वह है जहां आप या तो:

  1. एक बड़ी वस्तु को पास करना चाहते हैं (यानी, वस्तुओं / संरचना में इसके अंदर कई स्तरों पर वस्तुएं / संरचनाएं हैं) और इसकी नकल करना महंगा होगा और
  2. आप एक फ्रेमवर्क, विंडोज एपीआई या अन्य एपीआई को कॉल कर रहे हैं जिसकी आवश्यकता है।

सिर्फ इसलिए नहीं कि आप ऐसा कर सकते हैं। यदि आप एक परम में मूल्यों को बदलना शुरू करते हैं और ध्यान नहीं दे रहे हैं, तो आप कुछ बुरा कीड़े द्वारा गधे में थोड़ा सा प्राप्त कर सकते हैं।

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


3
यदि आप "स्वैप" को लागू करने की योजना बनाते हैं, तो रिफ से गुजरना मददगार हो सकता है।
ब्रायन

@ यदि मैं छोटी वस्तुओं के लिए रेफरी कीवर्ड का उपयोग करता हूं तो क्या इससे कोई समस्या होगी?
मनिराजएसएस

@TechnikEmpire "लेकिन, कॉल किए गए फ़ंक्शन के दायरे में ऑब्जेक्ट में परिवर्तन कॉलर को वापस प्रतिबिंबित नहीं किया जाता है।" यह गलत है। अगर मैं किसी व्यक्ति को पास करता हूं SetName(person, "John Doe"), तो नाम संपत्ति बदल जाएगी और उस परिवर्तन को कॉलर को प्रतिबिंबित किया जाएगा।
एम। मिम्पेन

@ M.Mimpen टिप्पणी हटा दी गई। मैंने उस समय मुश्किल से C # उठाया था और स्पष्ट रूप से मेरे * $ $ बाहर बात कर रहा था। इसे मेरे संज्ञान में लाने के लिए धन्यवाद।

@ क्रिस - मुझे पूरा यकीन है कि एक "बड़ी" वस्तु महंगी नहीं है। यदि आप अभी इसे पास करते हैं तो आप अभी भी सिर्फ एक पॉइंटर ही पास कर रहे हैं?
इयान

3

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

उदाहरण के लिए बाइट सरणी पहले और बाद में उसी मेमोरी का एक पॉइंटर है, मेमोरी को केवल अपडेट किया गया है।

एंडपॉइंट संदर्भ वास्तव में पॉइंटर में पॉइंटर से बाहर के फ़ंक्शन को फ़ंक्शन के अंदर उत्पन्न एक नए उदाहरण में अपडेट कर रहा है।


3

संदर्भ के रूप में एक रेफरी के रूप में आप एक पॉइंटर पास कर रहे हैं के बारे में सोचो। रेफरी का उपयोग नहीं करने का मतलब है कि आप एक पॉइंटर को मान से पास कर रहे हैं।

बेहतर अभी तक, जो मैंने कहा था उसे अनदेखा करें (यह शायद भ्रामक है, विशेष रूप से मूल्य प्रकारों के साथ) और इस एमएसडीएन पृष्ठ को पढ़ें ।


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

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

2
एक गैर-रेफरी संदर्भ प्रकार संदर्भ द्वारा पारित नहीं किया जाता है। संदर्भ प्रकार का एक संदर्भ मूल्य द्वारा पारित किया जाता है। लेकिन अगर आप किसी चीज़ के लिए एक पॉइंटर के रूप में एक संदर्भ के बारे में सोचना चाहते हैं जिसे संदर्भित किया जा रहा है, तो मैंने जो कहा है वह समझ में आता है (लेकिन इस तरह से सोचना भ्रामक हो सकता है)। इसलिए मेरी चेतावनी।
ब्रायन

लिंक मृत है - इसके बजाय इसे आज़माएं - docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
GIVE-ME-CHICKEN

0

मेरी समझ यह है कि ऑब्जेक्ट क्लास से प्राप्त सभी ऑब्जेक्ट्स को पॉइंटर्स के रूप में पास किया जाता है जबकि साधारण प्रकार (इंट, स्ट्रक्चर) को पॉइंटर्स के रूप में पास नहीं किया जाता है और रेफ की आवश्यकता होती है। मैं न तो स्ट्रिंग के बारे में निश्चित हूं (क्या यह अंततः ऑब्जेक्ट क्लास से लिया गया है?)


यह कुछ स्पष्टीकरण का उपयोग कर सकता है। मूल्य और संदर्भ tyoes के बीच क्या अंतर है। उत्तर: पैरामीटर पर रेफ कीवर्ड का उपयोग करने के लिए प्रासंगिक क्यों है?
23

.Net में, पॉइंटर्स, टाइप पैरामीटर और इंटरफेस को छोड़कर सब कुछ ऑब्जेक्ट से लिया गया है। यह समझना महत्वपूर्ण है, कि "साधारण" प्रकार (सही ढंग से "मूल्य प्रकार" कहा जाता है) ऑब्जेक्ट से भी विरासत में मिला है। आप उस पर सही हैं: मान प्रकार (प्रति डिफ़ॉल्ट) मान द्वारा पारित किए जाते हैं, जबकि संदर्भ प्रकार संदर्भ द्वारा पारित किए जाते हैं। यदि आप एक नया लौटाने के बजाय एक मूल्य प्रकार को संशोधित करना चाहते थे, (इसे एक विधि के अंदर संदर्भ प्रकार की तरह संभालें), तो आपको उस पर रेफ कीवर्ड का उपयोग करना होगा। लेकिन यह खराब शैली है और आपको बस ऐसा नहीं करना चाहिए जब तक कि आप पूरी तरह से सुनिश्चित न हों कि आपको :)
boybubble

1
अपने प्रश्न का उत्तर देने के लिए: स्ट्रिंग ऑब्जेक्ट से ली गई है। यह एक संदर्भ प्रकार है जो एक मान प्रकार (प्रदर्शन और तार्किक कारणों के लिए) की तरह व्यवहार करता है
बूडबुल

0

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

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


-1

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

शामिल पैरामीटर डिफ़ॉल्ट रूप से मान द्वारा पारित किए जाते हैं। अच्छी पोस्ट जो विवरण में चीजों को समझाती है। http://yoda.arachsys.com/csharp/parameters.html

Student myStudent = new Student {Name="A",RollNo=1};

ChangeName(myStudent);

static void ChangeName(Student s1)
{
  s1.Name = "Z"; // myStudent.Name will also change from A to Z
                // {AS s1 and myStudent both refers to same Heap(Memory)
                //Student being the non-Primitive type
}

ChangeNameVersion2(ref myStudent);
static void ChangeNameVersion2(ref Student s1)
{
  s1.Name = "Z"; // Not any difference {same as **ChangeName**}
}

static void ChangeNameVersion3(ref Student s1)
{
    s1 = new Student{Name="Champ"};

    // reference(myStudent) will also point toward this new Object having new memory
    // previous mystudent memory will be released as it is not pointed by any object
}

हम कह सकते हैं (चेतावनी के साथ) गैर-आदिम प्रकार कुछ भी नहीं हैं, लेकिन पॉइंटर्स और जब हम उन्हें रेफ द्वारा पास करते हैं तो हम कह सकते हैं कि हम डबल सूचक पास कर रहे हैं


सभी मापदंडों को डिफ़ॉल्ट रूप से, C # में पारित किया जाता है। किसी भी पैरामीटर को संदर्भ द्वारा पारित किया जा सकता है। संदर्भ प्रकारों के लिए, जो मान पारित किया जाता है (या तो संदर्भ या मूल्य से) वह स्वयं एक संदर्भ है। यह पूरी तरह से स्वतंत्र है कि इसे कैसे पारित किया जाता है।
सेविले

सहमत @Servy! "जब हम" संदर्भ "या" मूल्य "शब्दों को सुनते हैं, तो हमें यह ध्यान में रखना चाहिए कि क्या हमारा मतलब है कि एक पैरामीटर एक संदर्भ या मूल्य पैरामीटर है, या क्या हमारा मतलब है कि शामिल प्रकार एक संदर्भ या मूल्य प्रकार है 'लिटिल मेरी ओर से भ्रम की स्थिति है, जब मैंने कहा कि ग्राउंड जीरो रूल पहले, प्राइमिटिव्स वैल्यू (स्टैक) और नॉन-प्रिमिविटेबल बाई रेफरेंस (हीप) से मेरा मतलब है, जिसमें मैं शामिल था, न कि पेरीमीटर! जब हम पैरामीटर्स के बारे में बात करते हैं तो आप सही होते हैं! , सभी मापदंडों को C # में डिफ़ॉल्ट रूप से मान दिया जाता है।
सुरेंद्र सिंह मलिक

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

1
आप संदर्भ या मान से पैरामीटर पास करते हैं। "संदर्भ द्वारा" और "मूल्य के आधार पर" शब्दों का उपयोग यह बताने के लिए नहीं किया जाता है कि एक प्रकार एक मूल्य प्रकार है या एक संदर्भ प्रकार है।
१।

1
नहीं, यह धारणा की बात नहीं है। जब आप किसी चीज को संदर्भित करने के लिए गलत शब्द का उपयोग करते हैं तो वह कथन गलत हो जाता है । अवधारणाओं को संदर्भित करने के लिए सही शब्दावली का उपयोग करना सही कथनों के लिए महत्वपूर्ण है।
सेविली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.