2010-09-14 31 views
7

Null etiketli koleksiyonum var.Koleksiyondan farklı bir yineleme değişkeni var mı?

Derleyici neden yineleme değişkeni int türünde değil int??

 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 
     } 

Tabii ben yinelemenize ne dikkat etmelidir ancak derleyici burada gibi beni :) azarladı eğer iyi olurdu: C# derleyicisi sizin için Nullable<T> dereferences

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

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

Hata bana normal görünüyor. Int'den dönüştürebilirsiniz? int için, ancak bir int dönüştürebilirsiniz. –

+0

@Adrian Bu açıklamanın doğru olduğunu düşünmüyorum. Ya ben bool yerine dize olsaydım. Int dizeye dönüştürülebilir, ancak derleyici protesto ederdi. – nan

+1

Int, dizeye dönüştürülemiyor. Kesin veya açık bir dönüşüm tanımlanmamıştır. ToString() yöntemine başvurursanız, bu farklı bir şeydir. –

cevap

3

Güncelleme

Tamam, başlangıçta ben "derleyici bir foreach döngü atmalarını ekler." Dedi Bu kesinlikle kesinlikle değil: her zaman döküm eklemek olmaz. İşte gerçekten neler oluyor. Bu foreach döngü var olduğunda

Her

Öncelikle:

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) 
{ 
} 

... Burada derleyici yarattıklarından (sözde C#) temel taslak olduğunu Ne? Duyduğunuzu duyuyorum. Eğer [object] derken neyi kastediyorsunuz?" İşte

demek istiyorum. foreach döngü aslında aslında biraz büyülü demektir hiçbir arayüz gerektirir. Bu sadece bir ortaya nesnenin tipi de numaralandırılmış gerektirir e tipi mutlaka bir IEnumerator<int> uygulanması, hattaolmak zorunda değildir, çünkü kendisi de bir MoveNext ve Current özelliğini sağlayan bazı türünün bir örneği sağlaması gerekir GetEnumerator yöntemi.

yüzden [object] yazdım- aynı zamanda IDisposable'u (dolayısıyla [dispose if necessary] kısmı) uygulamak zorunda olmadığı anlamına da gelir.

Bu soruyu yanıtlamak amacıyla önemsediğimiz kodun bir kısmı, yazdığım bölümdür. Açıkça, derleyici gerçek bir IEnumerator<T> veya IEnumerator uygulama gerektirmediğinden, e.Current türünün T, object veya aralarında bir şey olduğu kabul edilemez.Bunun yerine derleyici, derleme zamanında GetEnumerator tarafından döndürülen türüne göre e.Current türünü belirler. Daha sonra, aşağıdaki işlemler gerçekleştirilir: tip yerel değişken (yukarıdaki örnekte x) türüdür

  1. ise düz bir atama için kullanılır. tip (I yasal döküm x tipine e.Current türünden var anlamına hangi) yerel bir değişken tipine dönüştürülebilir ise
  2. , bir döküm sokulur.
  3. Aksi takdirde, derleyici bir hata oluşturur.

Yani bir List<int?> üzerinde sıralandıran senaryosunda, biz 2. adıma olsun ve derleyici List<int?>.Enumerator türünün Current özelliği açıkça int için dökülebilir tip int? ait olduğunu görür.

Yani çizgi bunu eşdeğer derlenebilir: Şimdi

x = (int)e.Current; 

, ne yaptığını Nullable<int> için benzeri explicit operatör görünüyor? Reflektör göre

:

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

Yani the behavior described by Kent bildiğim kadarıyla söyleyebilirim, olduğunu, sadece bir derleyici optimizasyon: (int)e.Current açık döküm satır içi.

Sorunuza verilen genel bir yanıt olarak, derleyicimin ekleyeceği yönergelere göre, gerektiğinde foreach döngüsüne atıyorum.


jenerik önce * hayır IEnumerable<T> arayüzü, sadece IEnumerable olduğunu basit bir nedenle bir foreach döngü içinde gerekli yerlerde derleyici otomatik ekler

atmalarını Orijinal Cevap. IEnumerable arabirimi, IEnumerator ortaya çıkarır ve bu da object türünde Current özelliğine erişim sağlar.

Derleyici sizin için cast gerçekleştirmedikçe, eski zamanlarda, foreach kullanmış olmanızın tek yolu, object türünde yerel bir değişkenin olmasıydı, ki bu açıkça emiliyordu.

* Ve aslında, foreach doesn't require any interface at all - sadece GetEnumerator yöntemi ve bir MoveNext ve Current ile eşlik eden türü.

+0

Varsayılan olarak, IEnumerable '' GetEnumerator' adlı çağrılacak, 'foreach' jenerik'i yapmıyor mu? – strager

+0

@strager: Evet, ancak yine de bir oyuncu olacak. – SLaks

+0

@Slaks: Oyuncular yok ve jeneriklerin bazı performans avantajlarını olumsuz etkiliyor.Mesajımı kontrol et. –

4

çünkü.

bu kodu yazarsanız:

 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()); 
     } 

C# derleyicisi üretir:

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()); 
} 

Not Nullable<int>.Value açık dereferencing. Bu, çalışma süresinde Nullable<T> yapısının nasıl yerleşik olduğuna dair bir kanıttır.

+1

Sağladığınız bağlantı, operatörün "açık" olduğunu açıkça belirtir. Bir şey mi özlüyorum ... – strager

+0

@strager: Hayır, haklısın. 'T' -> Nullable 'bu örtük. Cevabımı güncellediniz. –

+0

"Nullable ' yapısının çalışma zamanında nasıl aşılandığının bir kanıtıdır. " Bunun çalışma zamanı ile ne ilgisi olduğunu göremiyorum. Bence cevabın sonucun dışında doğru. – strager

3

Gözlemlediğiniz davranış, 8.8.4 C# dil belirtiminin foreach ifadesine göre bölümdür. Bu bölüm, foreach ifadesinin anlamlarını aşağıdaki gibi tanımlar:

[...] Başarılı olursa, yukarıdaki adımlar, C numaralı bir koleksiyon türü, E numaralı numaralayıcı ve T öğe türünü oluşturur. koleksiyon türü, List<int?> olacağını sizin numunede, şartnamesinde tanımlanan kurallara göre

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

: Formun bir foreach açıklamada

foreach (V v in x) embedded-statement 

daha sonra genişletilir numaralandırıcısıList<int?>.Enumerator olur ve öğe türü int? olur.

Yukarıdaki kod snippet'ine bu bilgileri girerseniz, int?'un Nullable<T> Explicit Conversion (Nullable<T> to T) numaralı telefonu arayarak int'a açıkça gönderildiğini görürsünüz. Bu açık döküm operatörünün uygulanması, Kent tarafından açıklandığı gibi, Nullable<T>.Value özelliğini iade etmektir.

+0

Gah, temelde aynı zemini kendi başınıza ele aldığınızı anlamak için cevabımı çok uzun süre güncelledim! Yine de bir şey: Bence örnekte * numaralayıcı türü * aslında ' .Enumerator', * değil * 'IEnumerator '. Ben haklı mıyım –

+0

@Dan Tao: Evet, haklısınız (spekleri anladığım kadarıyla). –

İlgili konular