2012-05-25 22 views
5

कल मैंने इस अजीब मामले में ठोकर खाई, जहां t as D एक गैर-शून्य मान देता है, लेकिन (D)t एक कंपाइलर त्रुटि का कारण बनता है।यह एक्स अमान्य क्यों है जब 'x as y` ठीक काम करता है?

के बाद से मैं जल्दी मैं सिर्फ t as D का इस्तेमाल किया और आगे बढ़ाया में था, लेकिन मैं, क्यों डाली अमान्य है के बारे में उत्सुक हूँ के रूप में t वास्तव में एक D है। क्या किसी ने कुछ प्रकाश डाला है कि संकलक को कास्ट पसंद नहीं है?

class Program 
{ 
    public class B<T> where T : B<T> { } 

    public class D : B<D> { public void M() { Console.Out.WriteLine("D.M called."); } } 

    static void Main() { M(new D()); } 

    public static void M<T>(T t) where T : B<T> 
    { 
     // Works as expected: prints "D.M called." 
     var d = t as D; 
     if (d != null) 
      d.M(); 

     // Compiler error: "Cannot cast expression of type 'T' to type 'D'." 
     // even though t really is a D! 
     if (t is D) 
      ((D)t).M(); 
    } 
} 

संपादित करें: आसपास बजाना, मुझे लगता है कि यह एक स्पष्ट उदाहरण है। दोनों मामलों में tB होने के लिए बाध्य है और शायद D है। लेकिन जेनेरिक के साथ मामला संकलित नहीं होगा। क्या सी # केवल सामान्य बाधा को अनदेखा करता है जब यह निर्धारित करता है कि कास्ट कानूनी है या नहीं? भले ही यह इसे अनदेखा करता है, t अभी भी D हो सकता है; तो यह रनटाइम अपवाद के बजाय संकलन समय त्रुटि क्यों है?

class Program2 
{ 
    public class B { } 

    public class D : B { public void M() { } } 

    static void Main() 
    { 
     M(new D()); 
    } 

    public static void M(B t) 
    { 
     // Works fine! 
     if (t is D) 
      ((D)t).M(); 
    } 

    public static void M<T>(T t) where T : B 
    { 
     // Compile error! 
     if (t is D) 
      ((D)t).M(); 
    } 
} 
+2

शर्त '(डी) (ऑब्जेक्ट) t' काम करता है –

+0

संभावित डुप्लिकेट [प्रकार 'टी' का मूल्य परिवर्तित नहीं किया जा सकता है] (http://stackoverflow.com/questions/4092393/value-of-type-t-cannot-be-converted- करने के लिए) –

+1

[इस लिंक] में एक टिप्पणी से (http://stackoverflow.com/questions/1613314/generic-type-casting-method-net) मुझे [आपके उत्तर] का एक लिंक मिला (http: // bl ogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx)। अनिवार्य रूप से, कुछ परिवर्तनीय प्रकार हैं जिन्हें अन्य प्रकारों में नहीं डाला जा सकता है (अधिक विशेष रूप से बॉक्सिंग प्रकारों को कास्टिंग करने के नियम हैं)। चूंकि संकलक को इस बारे में कोई जानकारी नहीं है कि टी संकलन समय पर क्या है, इसे इसे सुरक्षित रखना और कलाकारों से इनकार करना है। –

उत्तर

3

अपने दूसरे उदाहरण में आप

((D)t).M(); 

को
((D)((B)t)).M(); 

आप D को B से डाल सकता बदल सकते हैं, लेकिन आप जरूरी "है कि एक B कुछ" करने के लिए से डाली नहीं कर सकता D। "कुछ जो B है" A हो सकता है, उदाहरण के लिए, यदि A : B

संकलक त्रुटि तब होती है जब आप संभावित रूप से पदानुक्रम में बच्चे से बच्चे तक कूदते हैं। आप पदानुक्रम को ऊपर और नीचे डाल सकते हैं, लेकिन आप इसे पार नहीं कर सकते हैं।

((D)t).M();  // potentially across, if t is an A 
((D)((B)t)).M(); // first up the hierarchy, then back down 

सूचना भी है कि आप अभी भी ((D)((B)t)).M(); अगर t के साथ एक रनटाइम त्रुटि मिल सकता है वास्तव में एक D नहीं है। (अपने is जांच हालांकि इस रोकने चाहिए।)

(यह भी ध्यान न मामले में संकलक खाते में if (t is D) जांच लेने है।)

एक पिछले उदाहरण:

class Base { } 
class A : Base { } 
class C : Base { } 

... 
A a = new A(); 
C c1 = (C)a;   // compiler error 
C c2 = (C)((Base)a); // no compiler error, but a runtime error (and a resharper warning) 

// the same is true for 'as' 
C c3 = a as C;   // compiler error 
C c4 = (a as Base) as C; // no compiler error, but always evaluates to null (and a resharper warning) 
+0

तो, क्यों 'संकलन' करता है? इस मामले में कास्ट और 'as' के बीच क्या अंतर है (_beside_ इसके रन-टाइम व्यवहार)? –

+0

'as' एक ही स्थिति में एक ही त्रुटि देता है। संपादित करें: वास्तव में, ओपी के उदाहरण में यह नहीं है। इसके साथ कुछ और खेलना होगा। –

+0

'as' एक पूरी तरह से अलग जानवर है। [यह msdn लिंक] देखें (http://msdn.microsoft.com/en-us/library/cscsdfbt%28v=vs.80%29.aspx) - यह 'अभिव्यक्ति प्रकार के बराबर है? (प्रकार) अभिव्यक्ति: (प्रकार) शून्य ' –

1

public static void M<T>(T t) where T : D 

को बदलें यह यह उचित प्रतिबंध नहीं है अगर आप जनादेश है कि टी प्रकार की होनी चाहिए डी

है चाहता हूँ

यह संकलन नहीं होगा अगर अपने प्रतिबंध को परिभाषित करता है टी टी

पूर्व के विकास के रूप में: आप List<int> to int डाली नहीं कर सकते हैं या ठीक इसके विपरीत

+0

वह यह नहीं पूछता कि इसे कैसे ठीक किया जाए लेकिन यह संकलक द्वारा क्यों खारिज कर दिया गया है। – Stilgar

+0

अद्यतन उत्तर। – TGH

0

आपके टेम्पलेट फ़ंक्शन में एक बाधा है जिसके लिए TB<T> होने की आवश्यकता है।

तो, अपने संकलक D के प्रकार T की वस्तु t कन्वर्ट करने के लिए कोशिश करता है जब यह यह नहीं कर सकते। क्योंकि TB<T> होने के लिए guranteed है, लेकिन D नहीं है।

यदि आप TD की आवश्यकता के लिए बाधा डालते हैं, तो यह काम करेगा। यानी where T: B<T>, D या बस where T: D जो यह भी गारंटी देता है कि TB<T> है, विरासत श्रृंखला का उपयोग।

और प्रश्न का दूसरा भाग: जब आप t as D पर कॉल करते हैं तो यह रनटाइम पर चेक किया जाता है। और, रनटाइम पर, पॉलिमॉर्फिज्म का उपयोग करके, यह सत्यापित किया गया है कि t को D प्रकार में परिवर्तित किया जा सकता है, और यह त्रुटियों के बिना किया जाता है।

नोट: वैसे, यह कोड बहुत अजीब है। क्या आप निश्चित हैं कि आप क्या कर रहे हैं?

+1

कास्ट रनटाइम पर भी चेक किए जाते हैं, कई मामलों में –

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