2009-07-30 6 views
112

ले लो निम्नलिखित:पॉलिमॉर्फिज्म का समर्थन क्यों नहीं करते 'रेफरी' और 'आउट' नहीं करते?

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 तर्क दोनों के साथ होता है।

उत्तर

155

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

अद्यतन:

Why do ref and out parameters not allow type variation?

अधिक के लिए ब्लॉग पेज देखें: मैं इस ब्लॉग प्रविष्टि के लिए आधार के रूप में इस उत्तर के लिए इस्तेमाल किया इस मुद्दे पर टिप्पणी। महान सवाल के लिए धन्यवाद।

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

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

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


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

नहीं। यही कारण है कि चर एक Turtle हो सकता है, लेकिन M समझेंगे कि यह केवल स्तनधारी हैं। TurtleMammal नहीं है।

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


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

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

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


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

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

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

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


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

हम्म।

अच्छा, क्यों नहीं? Nn से पढ़ नहीं सकता है, यह केवल इसे लिख सकता है, है ना? आप Tiger टाइप Animal के चर के लिए लिखते हैं और आप सब ठीक हैं, है ना?

गलत। नियम "N" n पर लिख सकता है "।

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

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

2) N पहले यह n से कुछ पढ़ता n के लिए कुछ लिखने के लिए है।

घटनाओं के इस क्रम परमिट कि:

  • प्रकार Animal के एक क्षेत्र x घोषित।
  • xout पैरामीटर N पर पैरामीटर के रूप में x पास करें।
  • NTigern में लिखता है, जो x के लिए उपनाम है।
  • किसी अन्य धागे पर, कोई Turtlex में लिखता है।
  • Nn की सामग्री को पढ़ने का प्रयास करता है, और यह Turtle को Mammal प्रकार का एक चर है।

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

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


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

यदि मूलभूत सिद्धांत सिद्धांत में इन मुद्दों में आपकी रुचि है, तो my series on how covariance and contravariance work in C# 4.0 पढ़ने पर विचार करें।

+6

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

+4

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

+0

इस मामले में, हम वास्तव में सार वर्ग चर का उपयोग तर्क के रूप में नहीं कर सकते हैं और इसके व्युत्पन्न क्लास ऑब्जेक्ट पर पास कर सकते हैं !! –

27

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

आप foo2 विधि में संदर्भ के रूप में ख पारित करने के लिए प्रयास करें, और foo2 में आप एक = नए ए() assing करने की कोशिश करते हैं, तो यह अवैध होगा।
एक ही कारण है कि आप नहीं लिख सकते हैं:

B b = new A(); 
+1

4 सेकंड द्वारा Ninj'd! :) – CannibalSmith

+0

+1 सीधे बिंदु पर और पूरी तरह से कारण बताता है। –

2

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

4

पर विचार करें:

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

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

B b = null; 
Foo2(ref b); 

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

+0

यह समस्या है कि var की वजह से यह "बी" का अस्पष्ट अनुमानित प्रकार है। –

+0

मुझे लगता है कि लाइन 6 पर आपका मतलब है => बी बी = शून्य; –

+0

@amiralles - हाँ, वह 'var' पूरी तरह से गलत था। फिक्स्ड। –

9

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

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

0

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

Foo2(ref (A)b) 
+0

ऐसा नहीं कर सकता, "एक रेफरी या आउट तर्क एक असाइन करने योग्य चर होना चाहिए" –

0

एक सुरक्षा के नजरिए से समझ बनाता है, लेकिन मैं इसे पसंद किया है | अगर संकलक एक त्रुटि के बजाय एक चेतावनी दे दी है, के बाद से वहाँ संदर्भ द्वारा पारित polymoprhic वस्तुओं के वैध उपयोग हैं। जैसे

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(); 
} 

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

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


अन्य लोगों के साथ फिर से प्रयास करें:

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

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

आप एक OracleConnection से अधिक टॉप अपने SqlConnection की भरवां।

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

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