2010-09-14 10 views
7

मेरे पास शून्य के इंक का संग्रह है।संग्रह से अलग प्रकार के इटरेशन चर?

कंपाइलर पुनरावृत्ति चर को intint क्यों नहीं देता है??

 List<int?> nullableInts = new List<int?>{1,2,3,null}; 
     List<int> normalInts = new List<int>(); 


     //Runtime exception when encounter null value 
     //Why not compilation exception? 
     foreach (int i in nullableInts) 
     { 
     //do sth 
     } 

बेशक मैं मैं क्या माध्यम से पुनरावृति करने के लिए ध्यान देना चाहिए, लेकिन यह अच्छा होगा अगर संकलक मुझे :) फटकार भी लगाई यहाँ की तरह:

 foreach (bool i in collection) 
     { 
      // do sth 
     } 

     //Error 1 Cannot convert type 'int' to 'bool' 
+0

त्रुटि मुझे सामान्य लगती है। आप int से परिवर्तित कर सकते हैं? int करने के लिए, लेकिन आप एक int से बूल परिवर्तित नहीं कर सकते हैं। –

+0

@ एड्रियन मुझे नहीं लगता कि यह स्पष्टीकरण सही है। अगर मैं बूल के बजाय स्ट्रिंग था तो क्या होगा। Int को स्ट्रिंग में परिवर्तित किया जा सकता है लेकिन कंपाइलर विरोध प्रदर्शन करेगा। – nan

+1

Int को स्ट्रिंग में परिवर्तित नहीं किया जा सकता है। कोई अंतर्निहित या स्पष्ट रूपांतरण परिभाषित नहीं किया गया है। यदि आप ToString() विधि का जिक्र कर रहे हैं तो यह एक अलग बात है। –

उत्तर

3

अद्यतन

ठीक है, मूल रूप से मैं ने कहा, "संकलक एक foreach पाश करने के लिए डाले कहते हैं।" यह सख्ती से सटीक नहीं है: यह हमेशा कास्ट नहीं करेगा। वास्तव में क्या हो रहा है यहाँ है।

सभी की

सबसे पहले, आप इस foreach पाश है जब:

int x; 
[object] e; 
try 
{ 
    e = collection.GetEnumerator(); 
    while (e.MoveNext()) 
    { 
     x = [cast if possible]e.Current; 
    } 
} 
finally 
{ 
    [dispose of e if necessary] 
} 

:

foreach (int x in collection) 
{ 
} 

... यहाँ क्या संकलक बनाता है की बुनियादी रूपरेखा (में छद्म सी #) है क्या? मैं आपको सुन रहा हूँ। आप [object] से क्या मतलब है? "

यहाँ मैं क्या मतलब है। foreach पाश वास्तव में आवश्यकता है कोई इंटरफ़ेस है, जो यह वास्तव में एक छोटा सा जादुई है का मतलब है। यह केवल आवश्यकता है कि वस्तु के प्रकार के प्रगणित किया जा रहा एक को उजागर करता है GetEnumerator विधि, जो बदले में कुछ प्रकार है कि एक MoveNext और एक Current संपत्ति प्रदान करता है का एक उदाहरण प्रदान करनी चाहिए।

तो मैं [object] लिखा था क्योंकि e के प्रकार के जरूरी IEnumerator<int> के एक कार्यान्वयन, या यहाँ तक +०१२३८६५५९७७ होने की जरूरत नहीं है- जिसका अर्थ यह भी है कि इसे IDisposable (इसलिए [dispose if necessary] भाग) को लागू करना आवश्यक नहीं है।

इस प्रश्न का उत्तर देने के उद्देश्य से हम जिस कोड की परवाह करते हैं उसका वह हिस्सा वह हिस्सा है जहां मैंने [cast if possible] लिखा था। जाहिर है, क्योंकि कंपाइलर को वास्तविक IEnumerator<T> या IEnumerator कार्यान्वयन की आवश्यकता नहीं है, e.Current का प्रकार T, object या बीच में कुछ भी नहीं माना जा सकता है।इसके बजाए, संकलक संकलन समय पर GetEnumerator द्वारा लौटाए गए प्रकार के आधार पर e.Current का प्रकार निर्धारित करता है। फिर निम्न होता है:

  1. हैं प्रकार स्थानीय चर (उपरोक्त उदाहरण में x) के प्रकार, एक सीधे काम किया जाता है।
  2. हैं प्रकार परिवर्तनीय स्थानीय चर के प्रकार (जिसके द्वारा मेरा मतलब है, एक कानूनी डाली x के प्रकार के e.Current के प्रकार से मौजूद है) करने के लिए, एक डाली डाला जाता है।
  3. अन्यथा, कंपाइलर एक त्रुटि उठाएगा।

तो एक List<int?> से अधिक की गणना की स्थिति में, हम चरण 2 पर हो और संकलक देखता है कि List<int?>.Enumerator प्रकार के Current संपत्ति प्रकार int?, जो स्पष्ट रूप से int लिए डाली जा सकती है, की है।

तो लाइन इस के बराबर करने के लिए संकलित किया जा सकता:

x = (int)e.Current; 

अब, क्या करता है Nullable<int> के लिए की तरह explicit ऑपरेटर देखो?

परावर्तक के अनुसार:

public static explicit operator T(T? value) 
{ 
    return value.Value; 
} 

तो the behavior described by Kent, है जहाँ तक मैं बता सकता हूँ, बस एक संकलक अनुकूलन: (int)e.Current स्पष्ट डाली inlined है।

आपके प्रश्न के सामान्य उत्तर के रूप में, मैं अपने दावे से चिपक जाता हूं कि कंपाइलर आवेषण foreach लूप में आवश्यक है जहां आवश्यक हो।


मूल उत्तर

संकलक स्वचालित रूप से सम्मिलित करता डाले जहां सरल कारण के लिए एक foreach पाश में की जरूरत है कि जेनरिक से पहले वहाँ था कोई IEnumerable<T> इंटरफेस, केवल IEnumerable *। IEnumerable इंटरफ़ेस IEnumerator का खुलासा करता है, जो बदले में Current प्रकार object की संपत्ति प्रदान करता है।

तब तक जब तक कि संकलक ने आपके लिए कास्ट नहीं किया, पुराने समय में, foreach का उपयोग करने का एकमात्र तरीका object के स्थानीय चर के साथ किया गया था, जो स्पष्ट रूप से चूसा होता।

* और वास्तव में, foreach doesn't require any interface at all - केवल GetEnumerator विधि और एक MoveNext और एक Current साथ एक साथ लिखें।

+0

डिफॉल्ट रूप से, 'अनुमानित' सामान्य बनाने के लिए 'IENumerable '' GetEnumerator' नहीं कहा जाएगा? – strager

+0

@strager: हाँ, लेकिन अभी भी एक कलाकार होगा। – SLaks

+0

@ स्लक्स: कोई कलाकार नहीं है और कोई जेनेरिक के कुछ प्रदर्शन लाभों को अस्वीकार कर देगा।मेरी पोस्ट देखें। –

4

क्योंकि सी # संकलक आप के लिए Nullable<T> dereferences।

आप इस कोड लिखते हैं:

 var list = new List<int?>() 
     { 
      1, 
      null 
     }; 

     foreach (int? i in list) 
     { 
      if (!i.HasValue) 
      { 
       continue; 
      } 

      Console.WriteLine(i.GetType()); 
     } 

     foreach (int i in list) 
     { 
      Console.WriteLine(i.GetType()); 
     } 

सी # संकलक का उत्पादन:

foreach (int? i in list) 
{ 
    if (i.HasValue) 
    { 
     Console.WriteLine(i.GetType()); 
    } 
} 
foreach (int? CS$0$0000 in list) 
{ 
    Console.WriteLine(CS$0$0000.Value.GetType()); 
} 

नोट Nullable<int>.Value की स्पष्ट dereferencing। यह एक प्रमाण पत्र है कि Nullable<T> संरचना को रनटाइम में कैसे शामिल किया गया है।

+1

आपके द्वारा प्रदान किया गया लिंक स्पष्ट रूप से लिखता है कि ऑपरेटर 'स्पष्ट' है। क्या मैं कुछ भूल रहा हूँ...? – strager

+0

@strager: नहीं, आप सही हैं। यह 'टी' ->' Nullable 'है जो अंतर्निहित है। मेरा जवाब अपडेट किया है। –

+0

"यह एक प्रमाण पत्र है कि कैसे 'Nullable ' संरचना रनटाइम में है।" मुझे नहीं लगता कि इसे रनटाइम के साथ क्या करना है। मुझे लगता है कि आपका निष्कर्ष आपके निष्कर्ष को छोड़कर सही है। – strager

3

जो व्यवहार आप देख रहे हैं वह धारा 8.8.4 सी # भाषा विनिर्देश के foreach स्टेटमेंट के अनुसार है। यह खंड foreach कथन के अर्थशास्त्र को निम्नानुसार परिभाषित करता है:

[...] उपर्युक्त चरण, यदि सफल होते हैं, तो अनजाने में संग्रह प्रकार C, गणनाकर्ता E और तत्व प्रकार T उत्पन्न करते हैं।

{ 
    E e = ((C)(x)).GetEnumerator(); 
    try { 
     V v; 
     while (e.MoveNext()) { 
      v = (V)(T)e.Current; 
      embedded-statement 
     } 
    } 
    finally { 
     // Dispose e 
    } 
} 

नियम विनिर्देश में परिभाषित के अनुसार, अपने नमूना में संग्रह प्रकार, List<int?> होगा: प्रपत्र की एक foreach बयान

foreach (V v in x) embedded-statement 

तो करने के लिए विस्तारित किया गया है गणक प्रकारList<int?>.Enumerator होगा और तत्व प्रकार int? होगा।

यदि आप इस जानकारी को उपरोक्त कोड स्निपेट में भरते हैं तो आप देखेंगे कि int?Nullable<T> Explicit Conversion (Nullable<T> to T) पर कॉल करके int पर स्पष्ट रूप से डाला गया है। केंट द्वारा वर्णित इस स्पष्ट कलाकार ऑपरेटर का कार्यान्वयन, Nullable<T>.Value संपत्ति को वापस करने के लिए है।

+0

गह, मैंने अपने जवाब को अपडेट करने में काफी समय बिताया है, यह समझने के लिए कि आपने मूल रूप से उसी जमीन को अपने आप में कवर किया है! एक बात, हालांकि: मुझे लगता है कि उदाहरण में * एन्यूमेरेटर टाइप * वास्तव में 'सूची है। एन्यूमेरेटर', * नहीं * 'आईएन्यूमेरेटर '। क्या मैं सही हू? –

+0

@ दैन ताओ: हाँ, आप सही हैं (जैसा कि मैं कल्पना समझता हूं)। –

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