बहुरूपता का समर्थन 'रेफ' और 'आउट' क्यों नहीं करता?


124

निम्नलिखित लें:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

उपरोक्त संकलन-समय त्रुटि क्यों होती है? यह दोनों refऔर outतर्कों के साथ होता है।

जवाबों:


169

=============

अद्यतन: मैंने इस उत्तर को इस ब्लॉग प्रविष्टि के आधार के रूप में उपयोग किया है:

रेफरी और आउट पैरामीटर प्रकार भिन्नता की अनुमति क्यों नहीं देते हैं?

इस मुद्दे पर अधिक टिप्पणी के लिए ब्लॉग पेज देखें। महान प्रश्न के लिए धन्यवाद।

=============

मान लीजिए कि आप श्रेणियां होती हैं लगता है चलो Animal, Mammal, Reptile, Giraffe, Turtleऔर Tiger, स्पष्ट उपवर्गीकरण रिश्तों के साथ।

अब मान लीजिए आपके पास एक तरीका है void M(ref Mammal m)Mदोनों पढ़ और लिख सकते हैं m


क्या आप प्रकार Animalका एक चर पास कर सकते हैं M?

नहीं। उस चर में एक हो सकता है Turtle, लेकिन Mयह मान लेगा कि इसमें केवल स्तनधारी शामिल हैं। ए Turtleनहीं है Mammal

निष्कर्ष 1 : refमापदंडों को "बड़ा" नहीं बनाया जा सकता है। (स्तनधारियों की तुलना में अधिक जानवर हैं, इसलिए चर "बड़ा" हो रहा है क्योंकि इसमें अधिक चीजें हो सकती हैं।)


क्या आप प्रकार Giraffeका एक चर पास कर सकते हैं M?

सं Mको लिख सकते हैं m, और Mएक लिखने के लिए चाहते हो सकता है Tigerमें m। अब आपने Tigerएक वेरिएबल में डाल दिया है जो वास्तव में टाइप का है Giraffe

निष्कर्ष 2 : refपैरामीटर को "छोटा" नहीं बनाया जा सकता है।


अब विचार करें N(out Mammal n)

क्या आप प्रकार Giraffeका एक चर पास कर सकते हैं N?

नहीं Nलिख सकते हैं n, और Nएक लिखना चाहते हो सकता है Tiger

निष्कर्ष 3 : outपैरामीटर को "छोटा" नहीं बनाया जा सकता है।


क्या आप प्रकार Animalका एक चर पास कर सकते हैं N?

हम्म।

जरूर क्यों नहीं? Nसे नहीं पढ़ा जा सकता है n, यह केवल इसे लिख सकता है, है ना? आप एक Tigerप्रकार के चर के लिए लिखते हैं Animalऔर आप सभी सेट कर रहे हैं, है ना?

गलत। नियम " Nकेवल लिख सकता है n" नहीं है।

नियम हैं, संक्षेप में:

1) सामान्य रूप से रिटर्न Nसे nपहले लिखना है N। (यदि Nफेंकता है, तो सभी दांव बंद हैं।)

2) इससे पहले कि वह कुछ पढ़ता है कुछ Nलिखना है ।nn

घटनाओं के इस क्रम की अनुमति देता है:

  • एक xप्रकार का क्षेत्र घोषित करें Animal
  • के पैरामीटर के xरूप में पास outकरें N
  • Nएक लिखता है Tigerमें nहै, जिसके लिए एक उपनाम है x
  • एक और धागा पर, किसी एक लिखता है Turtleमें x
  • Nकी सामग्री को पढ़ने का प्रयास करता है n, और Turtleयह सोचता है कि यह एक परिवर्तनशील प्रकार है Mammal

स्पष्ट रूप से हम इसे अवैध बनाना चाहते हैं।

निष्कर्ष 4 : outमापदंडों को "बड़ा" नहीं बनाया जा सकता है।


अंतिम निष्कर्ष : न तो refऔर न ही outपैरामीटर उनके प्रकार भिन्न हो सकते हैं। अन्यथा करने के लिए सत्यापन प्रकार की सुरक्षा को तोड़ना है।

यदि बुनियादी प्रकार के सिद्धांत में ये मुद्दे आपकी रुचि रखते हैं, तो मेरी श्रृंखला को पढ़ने पर विचार करें कि C # 4.0 में covariance और contravariance कैसे काम करते हैं


6
+1। वास्तविक-विश्व स्तर के उदाहरणों का उपयोग करने वाले महान विवरण जो स्पष्ट रूप से मुद्दों को प्रदर्शित करते हैं (जैसे - ए, बी और सी के साथ व्याख्या करना यह प्रदर्शित करना कठिन बनाता है कि यह काम क्यों नहीं करता है)।
ग्रांट वैग्नर

4
मैं इस विचार प्रक्रिया को पढ़कर दीन महसूस करता हूं। मुझे लगता है कि मैं किताबों को वापस लेना बेहतर समझता हूं!
स्कॉट मैकेंजी

इस मामले में, हम वास्तव में सार वर्ग चर का उपयोग तर्कों के रूप में कर सकते हैं और इसकी व्युत्पन्न वर्ग वस्तु पर पास कर सकते हैं !!
प्रशांत चोलाचगुड़ा

फिर भी, outपैरामीटर को "बड़ा" क्यों नहीं बनाया जा सकता है? आपके द्वारा वर्णित अनुक्रम किसी भी चर पर लागू किया जा सकता है, न कि केवल outपैरामीटर चर। और Mammalइससे पहले कि वह इस पर विचार करने के लिए प्रयास करने के लिए पाठक के तर्क मूल्य को डालने की जरूरत है Mammalऔर निश्चित रूप से यह विफल हो सकता है अगर वह विचार नहीं कर रहा है
एस्टन

29

क्योंकि दोनों ही मामलों में आपको पैरामीटर को रेफ / आउट करने के लिए मूल्य प्रदान करने में सक्षम होना चाहिए।

यदि आप संदर्भ के रूप में Foo2 विधि में b पास करने का प्रयास करते हैं, और Foo2 में आप a = new A () असेडिंग करने का प्रयास करते हैं, तो यह अमान्य होगा।
वही कारण जो आप नहीं लिख सकते हैं:

B b = new A();

+1 सीधे बिंदु पर और पूरी तरह से अच्छी तरह से कारण बताता है।
रुई क्रेवेइरो

10

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

ओओपी भाषाएँ जो परंपरागत रूप से शेष प्रकार के होते हुए भी ऐसा करती दिखाई देती हैं, वे हैं "धोखा" (छिपी हुई गतिशील प्रकार की जाँच सम्मिलित करना, या जाँच करने के लिए सभी स्रोतों की संकलन-समय की परीक्षा की आवश्यकता); मौलिक विकल्प यह है: या तो इस सहसंयोजक को त्याग दें और चिकित्सकों की पहेली को स्वीकार करें (जैसा कि C # करता है), या एक गतिशील टाइपिंग दृष्टिकोण (बहुत पहले OOP भाषा, स्मॉलटॉक, किया) के रूप में, या अचल (एकल) की ओर बढ़ें असाइनमेंट) डेटा, जैसे कि कार्यात्मक भाषाएं करती हैं (अपरिवर्तनीयता के तहत, आप सहूलियत का समर्थन कर सकते हैं, और अन्य संबंधित पहेलियों से भी बच सकते हैं जैसे कि आप एक परस्पर-डेटा दुनिया में स्क्वायर उपवर्ग आयत नहीं बना सकते हैं)।


4

विचार करें:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

यह प्रकार-सुरक्षा का उल्लंघन करेगा


यह var की वजह से अधिक अस्पष्ट अनुमान प्रकार "b" है, क्योंकि यह समस्या है।

मुझे लगता है कि लाइन 6 पर आपका मतलब => बी बी = शून्य;
अलेजांद्रो मिरालेस

@amiralles - हाँ, यह varपूरी तरह से गलत था। फिक्स्ड।
हेनक होल्टरमैन

4

जबकि अन्य प्रतिक्रियाओं ने स्पष्ट रूप से इस व्यवहार के पीछे के तर्क को समझाया है, मुझे लगता है कि यह ध्यान देने योग्य है कि अगर आपको वास्तव में इस प्रकृति के कुछ करने की आवश्यकता है तो आप Foo2 को एक सामान्य विधि में बनाकर समान कार्यक्षमता को पूरा कर सकते हैं, जैसे:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

2

क्योंकि दे रही है Foo2एक ref Bएक विकृत वस्तु में परिणाम होगा क्योंकि Foo2केवल जानता है कि कैसे को भरने के लिए Aका हिस्साB


0

क्या यह नहीं है कि संकलक आपको बता रहा है कि आप वस्तु को स्पष्ट रूप से डालना चाहते हैं ताकि यह सुनिश्चित हो सके कि आपको पता है कि आपके इरादे क्या हैं?

Foo2(ref (A)b)

ऐसा नहीं कर सकते हैं, "एक रेफरी या आउट तर्क एक परिवर्तनीय चर होना चाहिए"

0

सुरक्षा के दृष्टिकोण से समझ में आता है, लेकिन अगर कंपाइलर ने त्रुटि के बजाय चेतावनी दी है, तो मैंने इसे प्राथमिकता दी होगी, क्योंकि संदर्भ द्वारा पारित पॉलीमॉर्फिक वस्तुओं के वैध उपयोग हैं। जैसे

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

यह संकलन नहीं करेगा, लेकिन यह काम करेगा?


0

यदि आप अपने प्रकारों के लिए व्यावहारिक उदाहरणों का उपयोग करते हैं, तो आप इसे देखेंगे:

SqlConnection connection = new SqlConnection();
Foo(ref connection);

और अब आपके पास अपना कार्य है जो पूर्वज ( यानी Object ) को लेता है :

void Foo2(ref Object connection) { }

संभवतः इसमें क्या गलत हो सकता है?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

तुम सिर्फ एक आवंटित करने में कामयाब रहे Bitmapअपने लिए SqlConnection

यह अच्छा नहीं है।


दूसरों के साथ फिर से कोशिश करें:

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

आपने अपना एक OracleConnectionओवर-टॉप भर दिया SqlConnection


0

मेरे मामले में मेरे कार्य ने एक वस्तु को स्वीकार किया और मैं कुछ भी नहीं भेज सका इसलिए मैंने बस किया

object bla = myVar;
Foo(ref bla);

और वह काम करता है

मेरा फू VB.NET में है और यह अंदर प्रकार की जांच करता है और बहुत सारे तर्क करता है

यदि मेरा उत्तर डुप्लिकेट है, तो मैं माफी चाहता हूं, लेकिन अन्य बहुत लंबे थे

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