90 votes

Pourquoi est-ce que je peux assigner 0.0 aux valeurs d'énumération, mais pas 1.0

Juste par curiosité: pourquoi puis-je assigner 0.0 à une variable de type énumération, mais pas 1.0? Regardez le code suivant:

 public enum Foo
{
    Bar,
    Baz
}

class Program
{
    static void Main()
    {
        Foo value1 = 0.0;
        Foo value2 = 1.0;   // This line does not compile
        Foo value3 = 4.2;   // This line does not compile
    }
}
 

Je pensais que les conversions entre les types numériques et les valeurs d'énumération ne sont autorisées que via des conversions? C'est-à-dire que je pourrais écrire Foo value2 = (Foo) 1.0; afin que la ligne 2 de Main puisse être compilée. Pourquoi existe-t-il une exception pour la valeur 0.0 en C #?

98voto

Eric Lippert Points 300275

Jon réponse est bonne. Je voudrais ajouter à cela les points suivants.

  • J'ai fait cette stupide et bug embarrassant. Beaucoup d'excuses.

  • Le problème a été causé par moi incompréhension de la sémantique d'une "expression est égal à zéro" prédicat dans le compilateur; je croyais que c'était de ne cocher que pour l'entier zéro de l'égalité, alors qu'en fait, il était la vérification de plus le long des lignes de "est-ce la valeur par défaut de ce type?" En fait, dans une version antérieure de ce bug, il était réellement possible d'affecter la valeur par défaut d'un type dans un enum! Il est maintenant seulement les valeurs par défaut des numéros. (Leçon: le Nom de votre helper prédicats avec soin.)

  • Le comportement que j'essayais de mettre en œuvre que j'ai foiré était en fait une solution de contournement pour un peu différente de bug. Vous pouvez lire l'ensemble de la terrible histoire ici: http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx et ici http://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx (Leçon: Il est très facile d'introduire de nouveaux pire des bugs lors de la fixation de vieux.)

  • L'équipe C#, a décidé de consacrer ce buggy comportement plutôt que de le fixer, car le risque de casser le code existant pour pas convaincante prestation était trop élevé. (Leçon: l'obtenir dès la première fois!)

  • Le code que j'ai écrit dans Roslyn pour préserver ce comportement peut être trouvé dans la méthode HasImplicitEnumerationConversion en compilers\csharp\source\binder\semantics\conversions\conversions.cs -- voir pour plus de détails de ce qu'est exactement le Roslyn comportement. (Notez que j'ai pris la leçon de nommer votre prédicats bien à cœur - HasImplicitEnumerationConversion, IsNumericType et IsConstantNumericZero tous les font exactement ce qu'ils disent sur la boite. J'ai écrit presque tout le code dans les Conversions répertoire; je vous encourage à lire tout ce qu'il y a beaucoup de faits intéressants au sujet de la façon dont C# diverge à partir de la spécification dans les commentaires. J'ai décoré chaque avec SPEC VIOLATION pour les rendre faciles à trouver.)

Un autre point d'intérêt: C# permet également de toute valeur d'enum pour être utilisé dans un enum initialiseur quelle que soit sa zeroness:

enum E { A = 1 }
enum F { B = E.A }  // ???

La spécification est un peu vague quant à savoir si cela devrait être légal ou non, mais encore une fois, comme cela a été dans le compilateur pour un long temps, les nouveaux compilateurs sont susceptibles de maintenir le comportement.

98voto

Jon Skeet Points 692016

C'est un bug que vous pouvez utiliser 0.0. Le compilateur implicitement traite toutes les expressions constantes avec une valeur de zéro que seulement 0.

Maintenant, c'est correct pour le compilateur pour permettre une conversion implicite d'une constante int de l'expression de 0 à votre enum conformément à l'article 6.1.3 de la C# 5 spécifications:

Une énumération implicite de conversion permet à la décimale d'un entier littéral de 0 à être convertis à tout enum type et de toute nullable-type dont le type sous-jacent est un enum type. Dans ce dernier cas, la conversion est évaluée par la conversion de la sous-enum type et de l'envelopper dans la suite (§4.1.10).

J'ai parlé avec le C# de l'équipe à ce sujet avant: ils aurais aimé avoir enlevé le accidentel de conversion de 0.0 (et en effet 0.0 m 0.0 f) pour les valeurs de l'enum, mais malheureusement, je le comprends cassé trop de code - même si elle ne doit jamais avoir été admis à la première place.

Le Mono mcs compilateur interdit tout flottement de ces conversions, bien qu'il ne le permettent:

const int Zero = 0;
...

SomeEnum x = Zero;

malgré le fait qu' Zero est une expression constante, mais pas une décimale d'un entier littéral.

Je ne serais pas surpris de voir la spécification C# changer à l'avenir pour permettre à un nombre entier d'expression constante avec une valeur de 0 (c'est à dire à imiter mcs), mais je ne m'attends pas à virgule flottante, les conversions à jamais officiellement être correct. (J'ai été mal, avant de prédire l'avenir de C#, bien sûr...)

10voto

Konrad Rudolph Points 231505

Les énumérations en C # sont par définition des valeurs intégrales. Pour des raisons de cohérence, C # ne doit accepter aucune de ces affectations, mais 0.0 est traité en silence comme une intégrale 0 . Ceci est probablement une retenue de C, où le littéral 0 été traité spécialement et peut prendre n'importe quel type donné - entier, nombre à virgule flottante, pointeur nul… vous l'appelez.

3voto

Phil Perry Points 1574

enum est vraiment prévu (dans toutes les langues qui la soutiennent) à une façon de travailler, de sens et de chaînes uniques (étiquettes) plutôt que des valeurs numériques. Ainsi, dans votre exemple, vous ne devriez utiliser la Barre et Baz lorsque vous traitez avec un Foo énumérés type de données. Vous ne devez jamais utiliser (à comparer avec le, ou céder) un entier, même si de nombreux compilateurs vous permettra de sortir avec elle (les énumérations sont généralement des entiers en interne), et dans ce cas, une 0.0 est négligemment traité comme un 0 par le compilateur.

Sur le plan conceptuel, il devrait être tout droit d'ajouter un entier n à une valeur énumérée, pour obtenir n valeurs plus bas de la ligne, ou de prendre val2-val1 pour voir à quelle distance ils sont, mais à moins que la spécification du langage explicitement le permet, je voudrais l'éviter. (Pensez à une valeur énumérée comme un C pointeur, vous pouvez l'utiliser). Il n'y a aucune raison de les énumérations ne pouvait pas être mis en œuvre avec des nombres à virgule flottante, et fixe incrément entre eux, mais je n'ai pas entendu parler de ce fait dans n'importe quelle langue.

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