C # में संदर्भ या मान से ऑब्जेक्ट पास करना


233

C # में, मैंने हमेशा सोचा है कि गैर-आदिम चर संदर्भ द्वारा पारित किए गए थे और मूल्य द्वारा पारित किए गए आदिम मूल्य।

इसलिए किसी भी गैर-आदिम वस्तु को विधि में पास करते समय, विधि में वस्तु के लिए किया गया कोई भी वस्तु पारित होने पर प्रभाव डालेगा। (सी # 101 सामान)

हालाँकि, मैंने देखा है कि जब मैं एक System.Drawing.Image ऑब्जेक्ट को पास करता हूं, तो ऐसा नहीं लगता है? यदि मैं किसी अन्य विधि में एक system.drawing.image ऑब्जेक्ट पास करता हूं, और उस ऑब्जेक्ट पर एक छवि लोड करता हूं, तो उस पद्धति को दायरे से बाहर जाने दें और कॉलिंग विधि पर वापस जाएं, वह छवि मूल ऑब्जेक्ट पर लोड नहीं होती है?

ऐसा क्यों है?


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

जवाबों:


502

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

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

इसलिए:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

मेरे पास एक लेख है जो इस बारे में अधिक विस्तार से बताता है । असल में, "पास से संदर्भ" का मतलब यह नहीं है कि आप क्या सोचते हैं इसका मतलब है।


2
आपका अधिकार, मुझे वह दिखाई नहीं दिया! मैं इमेज लोड कर रहा हूं = Image.FromFile (..) और वह वेरिएबल इमेज को रिप्लेस कर रहा था और ऑब्जेक्ट को नहीं बदल रहा था! :) बेशक।
माइकल

1
@ एडेम: बिल्कुल नहीं - कोई "पैरामीटर ऑब्जेक्ट" नहीं है, ऐसी वस्तु है जिसे पैरामीटर का मान संदर्भित करता है। मुझे लगता है कि आपको सही विचार मिला है, लेकिन शब्दावली मायने रखती है :)
जॉन स्कीट

2
यदि हम कीवर्ड refऔर outसी # से ड्रॉप करते हैं , तो क्या यह कहना ठीक है कि सी # मापदंडों को उसी तरह से पास करता है जैसे जावा हमेशा करता है अर्थात हमेशा मूल्य से। क्या जावा के साथ कोई अंतर है।
ब्रॉडबैंड

1
@broadband: हां, डिफ़ॉल्ट पासिंग मोड बाइ-वैल्यू है। हालाँकि बेशक C # में पॉइंटर्स और कस्टम वैल्यू टाइप हैं, जो इसे जावा की तुलना में थोड़ा अधिक जटिल बनाता है।
जॉन स्कीट

3
@ विप्पी: नहीं, बिल्कुल नहीं। यह संदर्भ की एक प्रति है । मेरा सुझाव है कि आप लिंक किए गए लेख को पढ़ें।
जॉन स्कीट

18

यह दिखाने के लिए एक और कोड नमूना:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestPlain(int i)
{
    i = 5;
}

public static void TestRef(ref int i)
{
    i = 5;
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

और आउटपुट:

TestPlain: 0

TestRef: 5

TestObjPlain: परीक्षण

TestObjRef: TestObjRef


2
यदि हम कॉलर फ़ंक्शन में परिवर्तन देखना चाहते हैं तो मूल रूप से संदर्भ प्रकार को अभी भी संदर्भ के रूप में पास किया जाना चाहिए।
अटूट

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

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

11

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

जब आप किसी आवृत्ति को तर्क के रूप में पास करते हैं copy, तो वह आवृत्ति से गुजरता है । अब, यदि आप जिस उदाहरण से गुजरते हैं वह एक है value type(उस में रहता है stack) आप उस मान की प्रति को पास करते हैं , इसलिए यदि आप इसे संशोधित करते हैं, तो यह कॉलर में परिलक्षित नहीं होगा। यदि उदाहरण एक संदर्भ प्रकार है, तो आप ऑब्जेक्ट की संदर्भ की प्रतिलिपि (फिर से रहता है stack) पास करते हैं। तो आपको एक ही वस्तु के दो संदर्भ मिले। वे दोनों वस्तु को संशोधित कर सकते हैं। लेकिन अगर विधि निकाय के भीतर, आप नई ऑब्जेक्ट को तुरंत संदर्भित करते हैं तो संदर्भ की आपकी प्रतिलिपि अब मूल ऑब्जेक्ट को संदर्भित नहीं करेगी, यह आपके द्वारा बनाई गई नई ऑब्जेक्ट को संदर्भित करेगी। तो आपके पास 2 संदर्भ और 2 ऑब्जेक्ट होंगे।


यह चुना हुआ उत्तर होना चाहिए!
जन

मैं पूरी तरह से सहमत हूँ! :)
जोसेफेट

8

जब आप इसे इस तरह से करते हैं, तो मुझे इसके स्पष्ट होने का अनुमान है। मैं इस तरह की चीजों का परीक्षण करने के लिए LinqPad डाउनलोड करने की सलाह देता हूं ।

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

और वह आउटपुट होना चाहिए

WontUpdate

पहला नाम: एगली, अंतिम नाम: बेसेरा

UpdateImplicitly

पहला नाम: फेवियो, अंतिम नाम: बेसेरा

UpdateExplicitly

पहला नाम: फेवियो, अंतिम नाम: बेसेरा


और सार्वजनिक स्थैतिक शून्य WhatAbout (व्यक्ति p) {p = new Person () {FirstName = "First", LastName = "Last"}; }। :)
मारिन पोपोव

4

जब आप एक System.Drawing.Imageप्रकार की वस्तु को एक विधि में पास करते हैं तो आप वास्तव में उस वस्तु के संदर्भ की एक प्रति पारित कर रहे हैं।

इसलिए यदि उस पद्धति के अंदर आप एक नई छवि लोड कर रहे हैं तो आप नए / कॉपी किए गए संदर्भ का उपयोग कर लोड कर रहे हैं। आप मूल में परिवर्तन नहीं कर रहे हैं।

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}

2

आपने विधि को ऑब्जेक्ट कैसे पास किया?

क्या आप वस्तु के लिए उस विधि के अंदर नया कर रहे हैं? यदि हां, तो आपको refविधि का उपयोग करना होगा ।

निम्नलिखित लिंक आपको बेहतर विचार देते हैं।

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html


-1

संदर्भ द्वारा पास में आप केवल फ़ंक्शन मापदंडों में "रेफरी" जोड़ते हैं और एक और चीज जिसे आपको फ़ंक्शन "स्टेटिक" घोषित करना चाहिए, क्योंकि मुख्य स्थिर (# public void main(String[] args)) है!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.