2009-07-30 13 views
32

निम्नलिखित संरचना को देखते हुए:सी # कंपाइलर बग? यह अंतर्निहित उपयोगकर्ता परिभाषित रूपांतरण संकलित क्यों नहीं करता है?

public struct Foo<T> 
{ 
    public Foo(T obj) { } 

    public static implicit operator Foo<T>(T input) 
    { 
     return new Foo<T>(input); 
    } 
} 

यह कोड संकलित करता है:

private Foo<ICloneable> MakeFoo() 
{ 
    string c = "hello"; 
    return c; // Success: string is ICloneable, ICloneable implicitly converted to Foo<ICloneable> 
} 

लेकिन यह कोड संकलित नहीं करता है - क्यों?

private Foo<ICloneable> MakeFoo() 
{ 
    ICloneable c = "hello"; 
    return c; // Error: ICloneable can't be converted to Foo<ICloneable>. WTH? 
} 

उत्तर

30

स्पष्ट रूप से, अंतर्निहित उपयोगकर्ता परिभाषित रूपांतरण काम नहीं करते हैं जब एक प्रकार का इंटरफ़ेस होता है। सी # चश्मा से:


6.4.1 अनुमति उपयोगकर्ता परिभाषित रूपांतरण यानी केवल कुछ उपयोगकर्ता परिभाषित

सी # परमिट घोषित किया जाना है। विशेष रूप से, पहले से मौजूद मौजूदा निहित या स्पष्ट रूपांतरण को फिर से परिभाषित करना संभव नहीं है। किसी दिए गए स्रोत प्रकार एस और लक्ष्य प्रकार टी के लिए, यदि एस या टी निरर्थक प्रकार हैं, तो S0 और T0 उनके अंतर्निहित प्रकारों को संदर्भित करें, अन्यथा S0 और T0 क्रमशः एस और टी के बराबर हैं।

  • S0 और T0 विभिन्न प्रकार हैं: एक वर्ग या struct लक्ष्य प्रकार टी केवल यदि निम्न में से सभी सत्य हैं करने के लिए एक स्रोत प्रकार एस से एक रूपांतरण घोषित करने के लिए अनुमति दी है।
  • या तो S0 या T0 कक्षा या संरचना प्रकार है जिसमें ऑपरेटर घोषणा होती है।
  • न तो एस 0 और न ही टी 0 एक इंटरफ़ेस-प्रकार है।
  • को छोड़कर उपयोगकर्ता परिभाषित रूपांतरण, एक रूपांतरण एस से मौजूद नहीं है टी करने के लिए या टी एस

के लिए अपना पहला विधि में, दोनों प्रकार के प्रकार के इंटरफेस नहीं हैं, इसलिए उपयोगकर्ता निहित परिभाषित रूपांतरण काम करता है।

चश्मा बहुत स्पष्ट नहीं हैं, लेकिन ऐसा लगता है कि यदि शामिल प्रकारों में से एक इंटरफ़ेस प्रकार है, तो संकलक किसी भी उपयोगकर्ता द्वारा परिभाषित निहित रूपांतरण को देखने का प्रयास नहीं करता है।

+0

वाह। क्या एक अजीब आवश्यकता है। मैं लिपर्ट या स्कीट या कुछ अन्य सी # विशेषज्ञ से सुनना चाहता हूं क्यों इंटरफ़ेस प्रकार इसके लिए काम नहीं करेंगे; निश्चित रूप से इस विषमता के पीछे एक अच्छा कारण होना चाहिए। –

+0

कुछ प्रयोग करने के बाद, मैं देखता हूं कि इंटरफेस यहां कुंजी प्रतीत होता है। अजीब बात यह है कि संकलक के लिए यह अक्सर समझने के लिए मानसिक छलांग लगती है कि अक्सर क्या करना है। हम्म। –

+0

मुझे पहले नमूना संकलन के बारे में एक स्पष्टीकरण में बहुत दिलचस्पी होगी। धारा 6.4.4 में नियमों को देखते हुए, मुझे नहीं लगता कि यह 'Foo ' रूपांतरण को चुनता है, क्योंकि 'आईसीएलनेबल' में 'स्ट्रिंग' शामिल नहीं है (चूंकि 'आईसीएलनेबल' एक इंटरफ़ेस है) और 'Foo ' है 'Foo ' से शामिल नहीं है (क्योंकि 'Foo ' से 'Foo ' तक कोई अंतर्निहित रूपांतरण नहीं है। शायद 6.1.9 "प्रकार पैरामीटर शामिल लागू रूपांतरण" किसी भी तरह खेल में आ रहा है? – zinglon

24

(स्वीकार किए जाते हैं जवाब की टिप्पणी पर बाद।)

हाँ, यह कल्पना की एक बहुत, बहुत भ्रामक हिस्सा है। विशेष रूप से "व्यापक प्रकार" के बारे में पूरी बात गहराई से त्रुटिपूर्ण है। मैं अब पूरे सालों को पूरी तरह से कुछ और सुसंगत रूप से फिर से लिखने का समय खोजने के लिए कई सालों से कोशिश कर रहा हूं लेकिन यह कभी भी पर्याप्त प्राथमिकता नहीं रहा है।

अनिवार्य रूप से जो हमने यहां प्राप्त किया है वह एक विरोधाभास है; हम कहते हैं कि इंटरफ़ेस से जुड़े उपयोगकर्ता-परिभाषित निहित रूपांतरण नहीं हैं, लेकिन स्पष्ट रूप से यह इस मामले में सत्य नहीं है; आईसी से Foo<IC> तक उपयोगकर्ता द्वारा परिभाषित अंतर्निहित रूपांतरण है, इस तथ्य के द्वारा प्रदर्शित किया गया है कि एक स्ट्रिंग Foo<IC> उस रूपांतरण के माध्यम से जाती है।

विशेष रूप से, यह करना संभव नहीं है पहले से मौजूद किसी अंतर्निहित या स्पष्ट रूपांतरण को फिर से परिभाषित:

क्या हम वास्तव में बेहतर बल होना चाहिए इस लाइन है कि आप उद्धृत है।

यही वह चीज है जो इस पूरी चीज को प्रेरित करता है; आपको यह सोचने की इजाजत नहीं है कि आप एक प्रतिनिधित्व-संरक्षित प्रकार परीक्षण कर रहे हैं जब वास्तव में आप उपयोगकर्ता द्वारा परिभाषित विधि को बुला रहे हैं।

interface IBar {} 
interface IFoo : IBar {} 
class Foo<T> : IFoo 
{ 
    public static explicit operator Foo<T>(T input) { whatever } 
} 
class Blah : Foo<IBar> {} 
... 
IBar bar = new Blah(); 
Foo<IBar> foo = (Foo<IBar>)bar; 

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

+0

ठीक है, इंटरफेस पर कास्टिंग के बारे में विशेष नियम, और इन नियमों का कारण, मुझे यह समझने में मदद करता है। स्पष्टीकरण के लिए धन्यवाद, एरिक। –

+0

मुझे जोड़ना चाहिए, मेरा कोड नमूना "महसूस करता है" जैसे इसे काम करना चाहिए। कॉन्वर्सिस की तरह, यह उन चीजों में से एक है जिसे आपके दिमाग में काम करना चाहिए, लेकिन नहीं। हे। –

+0

क्या इसका मतलब यह है कि ऊपर दिए गए उदाहरण में (Foo ) बार को स्पष्ट रूपांतरण के बजाय "कास्ट" के रूप में माना जाता है? –

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