2010-05-06 14 views
36

कोड के निम्नलिखित भाग में मुझे elements से baseElements पर अनुमानित रूप से डालने में सक्षम होने की उम्मीद है क्योंकि TBaseIBase पर स्पष्ट रूप से परिवर्तनीय है।क्या यह सी # 4 में एक कॉन्वर्स बग है?

public interface IBase { } 
public interface IDerived : IBase { } 
public class VarianceBug 
{ 
    public void Foo<TBase>() where TBase : IBase 
    { 
     IEnumerable<TBase> elements = null; 
     IEnumerable<IDerived> derivedElements = null; 
     IEnumerable<IBase> baseElements; 

     // works fine 
     baseElements = derivedElements; 

     // error CS0266: Cannot implicitly convert type 
     // 'System.Collections.Generic.IEnumerable<TBase>' to 
     // 'System.Collections.Generic.IEnumerable<IBase>'. 
     // An explicit conversion exists (are you missing a cast?) 
     baseElements = elements; 
    } 
} 

हालांकि, मुझे टिप्पणी में उल्लिखित त्रुटि मिलती है।

कल्पना से हवाला देते हुए:

एक प्रकार T<A1, …, An> विचरण-परिवर्तनीय एक प्रकार T<B1, …, Bn> अगर T या तो एक इंटरफेस या एक प्रतिनिधि प्रकार संस्करण प्रकार पैरामीटर T<X1, …, Xn> साथ घोषणा की, और प्रत्येक संस्करण प्रकार पैरामीटर के लिए है करने के लिए है निम्न में से Xi एक रखती है:

  • Xi covariant है और एक अंतर्निहित संदर्भ या पहचान रूपांतरणसे मौजूद हैBi को

  • Xi contravariant है और एक अंतर्निहित संदर्भ या पहचान रूपांतरण Bi से करने के लिए Ai

  • Xi अपरिवर्तनीय है मौजूद है और एक पहचान रूपांतरण Bi

जाँच करने के लिए Ai से मौजूद है मेरा कोड, यह spec के साथ संगत प्रतीत होता है:

  • IEnumerable<out T> एक अंतरफलक प्रकार

  • IEnumerable<out T> संस्करण प्रकार मानकों के साथ घोषित किया जाता है

  • TTBase से IBase

को covariant

  • एक अंतर्निहित संदर्भ रूपांतरण मौजूद है

    तो - क्या यह सी # 4 कंपाइलर में एक बग है?

  • +0

    जब आप स्पष्ट रूप से डाली तो क्या होगा? कंपाइलर का कहना है कि एक है। चूंकि आप इसे कम कर रहे हैं, थोड़े समझ में आता है ..? – flq

    +2

    बस इसे स्पष्ट करने के लिए - यह आपकी आखिरी गोली है "एक अंतर्निहित संदर्भ रूपांतरण टीबीज़ से आईबीएएस तक मौजूद है" जो असत्य है (जब तक आप 'वर्ग' नहीं जोड़ते)। यह असाइन किया जा सकता है, लेकिन यह * संदर्भ * एक संदर्भ-रूपांतरण नहीं है। 'वर्ग' के बिना यह एक "बाधित" रूपांतरण है, जो कुछ जादू है जो संदर्भ-प्रकारों और मूल्य-प्रकारों पर समान आईएल कॉल विधियों (संपत्ति एक्सेसर्स समेत) को उसी तरह देता है: http://msdn.microsoft .com/en-us/library/system.reflection.emit.opcodes.constrained.aspx –

    +0

    चार्ल्स: आप गलत हैं - पहला असाइनमेंट काम करता है (मेरी मशीन (टीएम) पर काम करता है)। –

    उत्तर

    49

    भिन्नता केवल संदर्भ-प्रकारों के लिए काम करती है (या पहचान रूपांतरण है)। यह ज्ञात नहीं है कि TBase संदर्भ प्रकार है जब तक आप : class जोड़ें:

    public void Foo<TBase>() where TBase : class, IBase 
    

    के बाद से मैं एक लिख सकते हैं:

    public struct Evil : IBase {} 
    
    +0

    अच्छा जवाब - वर्ग बाधा काम जोड़ना। हालांकि - यह एक और सवाल उठाता है: पहला असाइनमेंट क्यों काम करता है? –

    +3

    @ ओमर - क्योंकि 'आईबीएएस' और' आईडीरिव '* को संदर्भ के रूप में माना जाता है; यह केवल 'टीबीएस' है जो अनिश्चित है। –

    +0

    @MarcGravell: अधिक सटीक होने के लिए, जबकि इंटरफेस गैर-ढेर (मान) प्रकारों द्वारा कार्यान्वित किया जा सकता है, एक इंटरफ़ेस प्रकार का भंडारण स्थान * हमेशा * एक ढेर ऑब्जेक्ट संदर्भ रखेगा। उदाहरण के लिए, 'सूची > '' nnumerator 'को लागू करने वाले ढेर ऑब्जेक्ट्स के संदर्भ रखता है, जबकि' सूची जहां टी: आईएन्यूमेरेटर 'होगा, यदि' टी'' सूची है। एन्यूमेरेटर 'के उदाहरण हैं इसके बजाय उस प्रकार का संरचना प्रकार। ध्यान दें कि अगर किसी ने 'सूची <' नामक एक 'सूची >' में 'एन्यूमेरेटर' स्टोर करने का प्रयास किया है, तो सिस्टम अपने फ़ील्ड को एक नई ढेर ऑब्जेक्ट में कॉपी करेगा और इसके संदर्भ को संग्रहीत करेगा। – supercat

    13

    मार्क सही है - मैं तो बस के बारे में एक ही प्रतिक्रिया पेस्ट करने के लिए किया गया था।

    देखें सहप्रसरण & contravariance पूछे जाने वाले प्रश्न:

    http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

    पूछे जाने वाले प्रश्न से:

    "विचरण समर्थित है तभी एक प्रकार पैरामीटर एक संदर्भ प्रकार है।"

    विचरण मूल्य प्रकार के लिए समर्थित नहीं है

    निम्नलिखित संकलन नहीं करता है या तो:

    // int is a value type, so the code doesn't compile. 
    IEnumerable<Object> objects = new List<int>(); // Compiler error here. 
    
    +0

    अच्छा सहायक लिंक - धन्यवाद –

    +0

    और प्रश्न के साथ और अधिक, निम्नलिखित काम नहीं करेंगे: 'IENumerable तुलनात्मक = नई सूची ();' –

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