2012-01-18 8 views
19

मेरे पास ऐसी स्थिति है जहां मैं संकलक के व्यवहार को समझाऊंगा। हम Foo वर्ग के हस्ताक्षर में बदलाव करते हैं तोमुहरबंद कीवर्ड एक कास्ट पर संकलक की राय को प्रभावित करता है

static class FooGetterGetter 
{ 
    public static IFoo<T> Get<T>() 
    { 
     return (IFoo<T>)new FooGetter(); 
    } 
} 

और sealed कीवर्ड जोड़ने:

interface IFoo<T> 
{ 
    T Get(); 
} 

class FooGetter : IFoo<int> 
{ 
    public int Get() 
    { 
     return 42; 
    } 
} 

निम्नलिखित compiles और रन: एक छोटे से कोड को देखते हुए

sealed class FooGetter : IFoo<int> // etc 

फिर मुझे निम्न पंक्ति पर एक कंपाइलर त्रुटि मिलती है:

return (IFoo<T>)new FooGetter(); 
के

:

'MyNamespace.IFoo < टी >' के लिए प्रकार 'MyNamespace.FooGetter' कनवर्ट नहीं कर सकता

कोई व्याख्या कर सकते हैं क्या sealed कीवर्ड के संबंध में यहां हो रहा है? यह दृश्य स्टूडियो में एक .NET 4 परियोजना के खिलाफ # 4 सी है 2010

अद्यतन: काफी दिलचस्प है मैं व्यवहार के उस हिस्से पर ठोकर खाई जब मैं सोच रहा था क्यों निम्नलिखित कोड यह ठीक करता है जब sealed लागू किया जाता है:

return (IFoo<T>)(IFoo<int>)new FooGetter(); 

अद्यतन: सिर्फ स्पष्टीकरण के लिए, यह सब ठीक चलता है जब T के प्रकार का अनुरोध किया ठोस प्रकार के द्वारा प्रयोग किया T के प्रकार के रूप में ही है। यदि प्रकार भिन्न होते हैं, कलाकारों की तरह कुछ के साथ कार्यावधि में विफल रहता है:

प्रकार 'MyNamespace.StringFoo' की वस्तु कास्ट करने के लिए टाइप करने के लिए 'MyNamespace.IFoo`1 [System.Int32]'

असमर्थ

उपरोक्त उदाहरण में, StringFoo : IFoo<string> और कॉलर int प्राप्त करने के लिए कहता है।

+2

मैं इसका उत्तर नहीं है, लेकिन मैं कल्पना कर सकते हैं यह तथ्य यह है कि 'IFoo ' एक खुला सामान्य प्रकार जबकि 'FooGetter' लागू करता है के साथ क्या करना है कि कुछ 'IFoo 'जो एक बंद सामान्य प्रकार है। –

+1

बस एक नोट: मैंने सुनिश्चित किया कि मुझे प्रश्न पोस्ट करने से पहले व्यवहार परिभाषित किया गया है - मैं खुद को मूर्ख बनाना नहीं चाहता था :-) मैं देख सकता हूं कि इसकी अनुमति क्यों दी जा सकती है, संकलक यह गारंटी नहीं दे सकता कि क्या हो रहा है, यह केवल जानता है कि इसमें सफलता का मौका है। लेकिन किसी कारण से, यह सफलता का एक ही मौका हटा देता है जब मुहरबंद कीवर्ड मौजूद होता है क्योंकि यह माना जाता है कि क्योंकि इसे प्राप्त नहीं किया जा सकता है, यह टी से मेल नहीं खा सकता है। –

+0

+1 दिलचस्प :) – leppie

उत्तर

8

क्योंकि FooGetterIFoo<T> सामान्य रूप से लागू करने के बजाय IFoo<int> का एक स्पष्ट कार्यान्वयन है। चूंकि इसे सील कर दिया गया है, इसलिए संकलक जानता है कि int के अलावा कुछ भी सामान्य जेनर IFoo<T> पर डालने का कोई तरीका नहीं है। अगर इसे सील नहीं किया गया था, तो संकलक इसे Tint नहीं था, तो रनटाइम पर अपवाद को फेंकने और फेंकने की अनुमति देगा।

आप के अलावा और कुछ के साथ उपयोग करने का प्रयास करें एक int (जैसे FooGetterGetter.Get<double>();) आप एक अपवाद प्राप्त करें:

टाइप करने के लिए 'प्रकार की वस्तु कास्ट करने में असमर्थ' MyNamespace.FooGetter 'MyNamespace.IFoo`1 [System.Double] '।

क्या मैं के बारे में सुनिश्चित नहीं कर रहा हूँ क्यों संकलक करता नहीं गैर सील संस्करण के लिए कोई त्रुटि उत्पन्न है।आपके उप-वर्ग FooGetter जैसे new FooGetter() आपको कुछ भी दे सकता है जो IFoo<{something_other_than_int}> लागू करता है?

अद्यतन:

प्रति Dan Bryant और Andras Zoltan वहाँ तरीके हैं एक निर्माता से एक व्युत्पन्न वर्ग वापस जाने के लिए (या शायद अधिक के लिए ठीक संकलक विशेषताओं का विश्लेषण करके एक अलग प्रकार वापस जाने के लिए)। तो तकनीकी रूप से यह संभव है यदि कक्षा सील नहीं है।

+4

+1 लेकिन अपने अंतिम प्रश्न का उत्तर देने के लिए - ठीक है - यह एक इंटरफ़ेस है - तो आप बस इंटरफ़ेस का एक और उदाहरण जोड़ सकते हैं? कंपाइलर को 'नया' नहीं दिखाई देता है, यह केवल 'FooGetter' प्रकार की अभिव्यक्ति को देखता है, जो' सीलबंद 'नहीं होने पर किसी भी प्रकार से प्राप्त किया जा सकता है। –

+0

@DStanley मैं आपका बिंदु देखता हूं। आपकी स्पष्टीकरण तब सील किए गए कीवर्ड के चारों ओर जाने के लिए दो बार कास्टिंग करने पर भी लागू होती है। दो बार की स्थिति में, आप जानते हैं कि यदि आप कंक्रीट प्रकार 'int' का उपयोग करते हैं, तो आप' IFoo 'पर जा सकते हैं, तो आप उसी कारण से IFoo '' IFoo ' पर जा सकते हैं क्योंकि संकलक आपको ऐसा करने देता है 'FooGetter'' IFoo '(इसे सील नहीं किया गया था) - किसी कारण से यह रनटाइम पर किसी भी त्रुटि को उत्पन्न करने देता है। –

+3

एंड्रस के बिंदु को दूर करने के लिए, आपके पास 'सेकेंडफू: फूजीटर, आईएफयू ' हो सकता है। उत्सुक है कि यह उस मामले में 'नए' को क्यों अनदेखा करता है, जो किसी व्युत्पन्न प्रकार की गारंटी नहीं देता है ... जब तक कि यह मानता है कि आधार प्रकार संभवतः सही इंटरफ़ेस को कार्यान्वित कर सकता है और रनटाइम में त्रुटियों को छोड़ने से परेशान नहीं करता है। –

4

किसी भी व्युत्पन्न वर्ग सील की गयी में एक वर्ग IFoo<T> को लागू कर सकता है जब:

class MyClass : FooGetter, IFoo<double> { } 

जब FooGetter सील के रूप में चिह्नित किया जाता है, संकलक जानता है कि यह IFoo<T> के किसी भी अतिरिक्त कार्यान्वयन IFoo<int> के अलावा अन्य के लिए मौजूद हो सकता है के लिए संभव नहीं हो सकता है FooGetter

यह अच्छा व्यवहार है, यह आपको रनटाइम के बजाय संकलन समय पर आपके कोड के साथ समस्याओं को पकड़ने की अनुमति देता है।

कारण (IFoo<T>)(IFoo<int>)new FooGetter(); काम करता है क्योंकि अब आप अपनी सीलबंद कक्षा का प्रतिनिधित्व IFoo<int> के रूप में कर रहे हैं जिसे कुछ भी लागू किया जा सकता है। यह भी एक अच्छा काम है क्योंकि आप गलती से नहीं हैं, लेकिन उद्देश्यपूर्वक कंपाइलर चेक को ओवरराइड करते हैं।

1

बस मौजूदा उत्तरों में जोड़ने के लिए: इसका वास्तव में उपयोग किए गए जेनरिक के साथ कुछ लेना देना नहीं है।

interface ISomething 
{ 
} 

class OtherThing 
{ 
} 

फिर (एक विधि के अंदर) कह रही::

OtherThing ot = XXX; 
ISomething st = (ISomething)ot; 

काम करता है ठीक

इस सरल उदाहरण पर विचार करें। संकलक को पता नहीं है कि OtherThingISomething हो सकता है, इसलिए यह हमें विश्वास करता है जब हम कहते हैं कि यह सफल होगा। हालांकि, अगर हम OtherThing को सीलबंद प्रकार (अर्थात् sealed class OtherThing { } या struct OtherThing { }) में बदलते हैं, तो कलाकार को अब अनुमति नहीं है। कंपाइलर जानता है कि यह अच्छी तरह से नहीं जा सकता है (सिवाय otnull होना चाहिए, लेकिन सी # के नियम अभी भी उस मुहरबंद प्रकार से लागू किए गए इंटरफ़ेस तक एक सीलबंद प्रकार से कास्ट को अस्वीकार करते हैं)।

प्रश्न के अद्यतन के संबंध में: (IFoo<T>)(IFoo<int>)new FooGetter() लिखना (IFoo<T>)(object)new FooGetter() लिखने से बहुत अलग नहीं है। आप कुछ मध्यवर्ती प्रकार से जाकर किसी भी कलाकार (जेनेरिक या बिना) के "अनुमति" दे सकते हैं, जो कि निश्चित रूप से/संभावित रूप से उन दोनों प्रकार के पूर्वजों के बीच है जो आप बीच में परिवर्तित करना चाहते हैं। यह बहुत ज्यादा इस पद्धति के समान है:

void MyMethod<T>(T t) // no "where" constraints on T 
{ 
    if (typeof(T) = typeof(GreatType)) 
    { 
    var tConverted = (GreatType)(object)t; 
    // ... use tConverted here 
    } 
    // ... other stuff 
} 
संबंधित मुद्दे