33 votes

Conversion de type C # incohérente?

En C#, je ne peut pas convertir implicitement un long int.

long l = 5;
int i = l;  // CS0266: Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?)

Ce produit de ladite erreur. Et à juste titre; si je le fais, je suis au risque de me casser les données, due à des erreurs de troncature. Si je décide que je sais ce que je fais, alors je peux toujours faire un cast explicite, et de dire au compilateur que c'est correct pour tronquer, que je connais le mieux.

int i = (int)l;  // OK

Cependant, le même mécanisme ne semble pas s'appliquer lors de l'utilisation d'une boucle foreach.

IList<long> myList = new List<long>();
foreach (int i in myList)
{
}

Le compilateur n'a pas même de générer un avertissement ici, même si c'est essentiellement la même chose: une décoché la troncature d'un long int, qui peut très bien se casser mes données.

Donc ma question est simple: Pourquoi cette foreach pas créer la même erreur que l'affectation de variable n'?

30voto

Eric Lippert Points 300275

Mise à JOUR: Cette question a été l'objet de mon blog en juillet 2013. Merci pour la grande question!

Pourquoi est-ce foreach pas créer la même erreur que l'affectation de variable n'?

Le "pourquoi" des questions difficiles à répondre car je ne sais pas la "vraie" question posée. Ainsi, au lieu de répondre à la question, je vais répondre à certaines questions.

Ce que la section de la spécification justifie ce comportement?

Comme Michael Liu, en réponse à juste titre, il est de la section 8.8.4.

Le point de l'ensemble d'un explicite de conversion, c'est que la conversion doit être explicite dans le code; c'est pourquoi nous avons l'opérateur de cast; c'est en agitant un grand drapeau qui dit "il y en a une conversion explicite ici". C'est l'une des rares fois dans C#, où une conversion explicite n'existe pas dans le code. Quels sont les facteurs qui ont motivé l'équipe de conception de manière invisible, insérer "explicite" de la conversion?

L' foreach boucle a été conçu avant l'arrivée des génériques.

ArrayList myList = new ArrayList();
myList.Add("abc");
myList.Add("def");
myList.Add("ghi");

Vous ne voulez pas avoir à dire:

foreach(object item in myList)
{
    string current = (string)item;

Dans un monde sans les génériques que vous devez savoir à l'avance quels types sont dans une liste, et vous avez presque toujours n'avoir que des connaissances. Mais cette information n'est pas enregistrée dans le système de type. Par conséquent, vous devez indiquer au compilateur d'une certaine manière, et vous le faire en disant:

foreach(string item in myList)

C'est votre affirmation au compilateur que la liste est complète de chaînes, tout comme une fonte est une affirmation qu'un élément particulier est une chaîne de caractères.

Vous avez totalement raison que c'est une misfeature dans un monde avec des génériques. Car il serait en rupture de changer maintenant, nous sommes coincés avec elle.

La fonctionnalité est assez confus; quand j'ai commencé la programmation en C# j'ai supposé qu'il avait la sémantique de quelque chose comme:

while(enumerator.MoveNext())
{
    if (!(enumerator.Current is string) continue;
    string item = (string)enumerator.Current;

Qui est, "pour chaque objet de type string dans cette liste, procédez de la manière suivante", quand il est vraiment "pour chaque objet dans cette liste affirmer que l'élément est une chaîne de caractères et de faire ce qui suit..." (Si l'ancien est exactement ce que vous voulez, puis utilisez l' OfType<T>() méthode d'extension.)

La morale de l'histoire: les langues à la fin avec l'étrange "héritage" fonctionnalités lorsque vous massivement changer le type de système dans la version 2.

Si le compilateur produire un avertissement pour ce cas, un code moderne, où les médicaments génériques sont-ils utilisés?

Je considère que c'est. Notre recherche a montré que

foreach(Giraffe in listOfMammals)

est si commun que la plupart du temps, nous serait donner un avertissement pour le code est correct. Qui crée des ennuis pour tout le monde qui se compile avec "mises en garde comme des erreurs" est activée, et c'est en général la méchanceté d'avoir un avertissement sur le code qui est oui peut-être un peu malodorante, mais vraiment bon. Nous avons décidé de ne pas poursuivre l'avertissement.

Il existe d'autres situations où le compilateur C# invisible insère des conversions explicites?

Oui. En fait, quelqu'un a posé une question à ce sujet, juste quelques heures après celui-ci:

Compilateur remplace cast explicite à mon propre type de cast explicite .NET type?

Il y a quelques fort obscure de l'interopérabilité des scénarios où les conversions explicites sont insérés en tant que bien.

13voto

Michael Liu Points 15764

Comme défini dans le §8.8.4 de C# 4.0 spécifications, un foreach déclaration de la forme

foreach (V v in x) embedded-statement

est élargi à

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            v = (V)(T)e.Current; // <-- note the explicit cast to V
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

C est le "type de collection" et T est le "type d'élément" inférée à partir de x.

Le casting d' V (int dans votre cas) est ce qui permet à votre exemple pour compiler.

La raison probable pour le casting d' V: en C# 1.0, avant de génériques a été ajouté à la langue, un cast explicite est généralement nécessaire de toute façon lors de l'énumération à travers une collection comme ArrayList, parce que le compilateur ne pouvait pas automatiquement déterminer le type de valeurs dans la collection.

5voto

Ginosaji Points 620

La réponse simple est foreach effectue une distribution explicite en coulisse. Un autre exemple:

     public class Parent { }
    public class Child : Parent { }

    IList<Parent> parents = new List<Parent>()
    {
        new Parent()
    };
    foreach (Child child in parents) { }
 

Cela ne générera pas non plus d'erreur de compilation, mais générera un InvalidCastException au moment de l'exécution.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X