2009-06-04 7 views
19

Imaginer le code suivant:Foreach peut lancer une exception InvalidCastException?

class foreach_convert 
{ 
    public static void method2() 
    { 
     List<IComparable> x = new List<IComparable>(); 
     x.Add(5); 

     foreach (string s in x) 
     { 
      //InvalidCastException in runtime 
     } 
    } 
} 

Je me demande, pourquoi est-ce comportement foreach donc ... pas C# -comme? Ce qui se passe ici est une distribution implicite à une sous-classe, qui est sujette aux erreurs et semble être bannie dans tous les autres endroits de la langue. Ou n'ai-je pas raison?

P.S. La raison pour laquelle je demande est que j'ai eu un bug dans le code similaire dans mon projet, où je l'habitude de parcourir une collection personnalisée à partir d'une bibliothèque externe, appelée SomeTypeCollection, mais en fait fourni une collection d'éléments de type base et aurait pu contenir des éléments de SomeOtherType. Ma faute, mais ni la langue, ni le compilateur fourni des indices/avertissements explicites, ce qui est inhabituel pour C# ...

+6

Heh, j'ai d'abord rencontré ça quand j'espérais (pas vraiment) que foreach filtrerait implicitement les objets de la collection en fonction de leur type. Pensez à la façon dont il se lit, "Pour chaque chaîne de la collection." Pour moi, cela se lit comme, "Pour tout ce qui dans la collection est une chaîne, faites quelque chose." Il ignorerait alors tout ce qui n'était pas une chaîne. –

Répondre

22

repensez à avant génériques ... foreach dû jeter pour que vous puissiez faire des choses sensibles comme:

foreach (string x in names) 
{ 
    // ... 
} 

au lieu de:

foreach (object tmp in names) 
{ 
    string x = (string) tmp; 
    // ... 
} 

Ce dernier est juste dégueu, l'OMI. Fournir un casting implicite est différent du reste du langage, mais le rend beaucoup plus facile à utiliser dans la grande majorité des cas.

Je soupçonne que si C# avait génériques et méthodes d'extension pour commencer (afin que nous puissions utiliser OfType et Cast) que foreach ne serait pas spécifié exactement de la même manière.

Notez qu'il y a encore plus d'étrangeté dans foreach: le type ne doit pas implémenter IEnumerable du tout. Tant qu'il a une méthode GetEnumerator qui retourne quelque chose qui à son tour a MoveNext() et Current, le compilateur C# est heureux. Cela signifiait que vous pouviez implémenter un "itérateur fortement typé" (pour éviter la boxe) avant les génériques.

+0

Droite. Je soupçonne que c'est un héritage du tout premier C# et le rendre fortement typé est maintenant un changement de rupture ... [grumble] encore, il pourrait y avoir un avertissement ou juste une note dans MSDN :-) –

+0

Ok, plus rafraîchi maintenant, et capable de comprendre ... eh bien ... les mots ... ne pense pas que ma réponse a ajouté quelque chose qui n'est pas dit ici :) – jerryjvl

+1

Ils pourraient construire des fonctionnalités pertinentes sans génériques au lieu d'implémenter cette fonte implicite ridicule . – Trismegistos

9

Avec C# 3 j'utilise var - donc j'obtiens les avertissements du compilateur.

+0

Bon point. Utiliser var dans foreach peut vous sauver de ce bogue, car il détecterait le type exact de la collection que vous itérez. –

4

foreach fonctionne sur IEnumerable, qui renvoie des objets de type objet. Pour chaque article, l'objet sera casté au type que vous avez fourni.

Sauf si vous utilisez var en C# 3.0. Ensuite, le type sera pris de IEnumerable<T>, si elle est implémentée par la collection.

+2

Cela donne l'impression que foreach (int x dans la liste ) serait boîte puis unbox - ce qui n'est pas. Le compilateur C# utilise IEnumerable de préférence à IEnumerable où il est disponible. –

+0

vous avez raison à propos de l'interface dactylographiée.Parfois, les spécifications C# sont un peu incohérentes: il semble que la construction foreach soit la seule chose en C# qui permette la covariance et la contravariance. –

Questions connexes