web-dev-qa-db-fra.com

Quand utiliser Enums, et quand les remplacer par une classe avec des membres statiques?

Il s'est récemment occupé de moi que l'énumération suivante (échantillon) ...

enum Color
{
    Red,
    Green,
    Yellow,
    Blue
}

... pourrait être remplacé par une classe apparemment plus de sécurité:

class Color
{
    private Color() { }

    public static readonly Color   Red      = new Color();
    public static readonly Color   Green    = new Color();
    public static readonly Color   Yellow   = new Color();
    public static readonly Color   Blue     = new Color();
}

Avec "Type-Safe", je veux dire que la déclaration suivante fonctionnerait si Color était un énumé, mais pas si Color était la classe ci-dessus:

var nonsenseColor = (Color)17;    // works if Color is an enum

Deux questions:

1) Y a-t-il un nom largement accepté à ce modèle (remplaçant un énumé avec une classe de type-sécurité)?

2) dans lequel les cas devraient-ils utiliser enums, et quand une classe serait-elle plus appropriée?

Enums sont parfaits pour des informations sur l'état légère. Par exemple, votre couleur Enum (à l'exclusion du bleu) serait bonne pour interroger l'état d'un feu de circulation. La vraie couleur avec l'ensemble du concept de couleur et de tous ses bagages (alpha, espace de couleur, etc.) n'a pas d'importance, à quel état est la lumière dans. Aussi, changeez-vous un peu pour représenter l'état du feu :

[Flags()]
public enum LightColors
{
    unknown = 0,
    red = 1,
    yellow = 2,
    green = 4,
    green_arrow = 8
}

L'état lumineux actuel pourrait être défini comme suit:

LightColors c = LightColors.red | LightColors.green_arrow;

Et interrogé comme suit:

if ((c & LightColors.red) == LightColors.red)
{
    //Don't drive
}
else if ((c & LightColors.green_arrow) == LightColors.green_arrow)
{
    //Turn
}

Les membres de la couleur de la classe statique seraient en mesure de prendre en charge cet état multiple sans fonctionnalité supplémentaire.

Cependant, les membres de la classe statique sont merveilleux pour des objets couramment utilisés. Les System.Drawing.Color Les membres sont de grands exemples car ils représentent une couleur connue qui ont des constructeurs obscurs (sauf si vous ne connaissez pas vos couleurs hexagonales). S'ils ont été mis en œuvre comme Enums, vous devriez faire quelque chose comme ça à chaque fois que vous vouliez utiliser la valeur comme une couleur:

colors c = colors.red;
switch (c)
{
    case colors.red:
        return System.Drawing.Color.FromArgb(255, 0, 0);
        break;
    case colors.green:
        return System.Drawing.Color.FromArgb(0,255,0);
        break;
}

Donc, si vous avez une énumération et que vous trouvez que vous avez constamment effectué un commutateur/cas/si/sinon/quoi que ce soit pour dériver un objet, vous pouvez utiliser des membres de la classe statique. Si vous interrogez seulement l'état de quelque chose, je sert d'enums. De plus, si vous devez transmettre des données dans une mode dangereuse Enums survivra probablement mieux qu'une version sérialisée de votre objet.

Edit : @stakx, je pense que vous avez trébuché sur quelque chose d'important, aussi en réponse à la poste de @ Anton et que c'est complexité ou plus important encore, qui est complexe?

Du point de vue du consommateur, je préférerais énormément le système.Drawing.color Les membres de la classe statique devaient écrire tout cela. Du point de vue du producteur, cependant, ce serait une douleur de devoir écrire tout cela. Donc, si d'autres personnes vont utiliser votre code, vous pouvez les sauver beaucoup de problèmes en utilisant des membres de la classe statique même si cela pourrait vous prendre 10 fois plus longtemps pour écrire/tester/déboguer. Cependant, si c'est juste vous, vous pouvez trouver plus facilement d'utiliser des énumérums et de lancer/convertir au besoin.

27
Chris Haas

J'ai effectivement lu avec quelque chose comme ça un peu au travail.

Il était basé sur un exemple publié par John B dans Article de blog de Jon Skeet's "amélioré Enums en C #" .

Ce que je n'ai pas appris à travailler correctement sans beaucoup de laideur étendue à une énumération en dérivant une classe de dénombrement de base et en ajoutant des champs statiques supplémentaires du type dérivé.

Si vous vérifiez les bases publiées dans ce blog, vous remarquerez que les classes de dérivation partageront un ensemble commun de valeurs qui ont des problèmes de frappe graves et lorsqu'une classe de base de dénombrement est dérivée dans deux autres classes différentes, leurs ensembles de valeur seront dans la même collection aussi.

Je veux dire, vous seriez difficile de créer une variable de DeriveRenumClass1 et de devoir stocker une valeur de sa collection de dénombrement qui est en fait de type baseenumclass1 dans cette variable sans contournement de contournement.

Un autre problème était une sérialisation XML que nous utilisons beaucoup. Je me suis arrêté d'utiliser deux classes génériques comme des types de données sur des variables d'énumération sur nos objets métier: une qui représentait une seule valeur d'énumération et celle qui représentait un ensemble de valeurs d'énumération que j'ai utilisées pour imiter le comportement d'énumération des drapeaux. Ils ont géré la sérialisation XML et la désérialisation des valeurs réelles stockées dans les propriétés qu'ils représentaient. Un couple des surcharges d'opérateur était tout ce qui était nécessaire pour recréer le comportement de BitFlag de cette façon.

Ce sont une partie de la principale issure que j'ai rencontrée.

Dans l'ensemble, je ne suis pas très satisfait du résultat de fin, des trucs malodorants là-bas mais cela fait le travail pour l'instant.

Pourquoi notre architecte en chef a décidé d'essayer de toujours essayer de travailler était les possibilités d'avoir des classes réelles et de les prolonger avec toutes sortes de comportements, génériques ou spécifiques. Jusqu'à présent, je n'ai pas vu beaucoup, je n'aurais pas pu fournir des méthodes de vulgarisation, mais nous pourrions courir dans quelque chose.

Vous n'avez pas de code ici et je ne pourrai pas y arriver pour le week-end, sinon j'aurais pu montrer où j'étais allé avec elle. Pas très joli cependant ...: O

3
Anton

Certaines choses que j'ai trouvées entre-temps, si quelqu'un d'autre est intéressé:

  • switch blocks ne fonctionneront pas avec une classe ENUM.

  • Utilisateur EMPI mentionné la similitude de l'échantillon ci-dessus enum-as-classe à Java Enums. Il semble que dans le Java = monde, il y a un modèle reconnu appelé Typeafe Enum; Apparemment, ce modèle remonte au livre de Joshua Bloch Java efficace.

2
stakx

J'utiliserais les constantes de la classe lorsque je dois interoper avec un code hérité qui veut des valeurs d'indicateur de bits obscurs qui pourraient être ou ensemble. Dans ce cas, l'énum peut ressembler à

public enum Legacy : ushort
{
  SomeFlag1 = 0x0001,
  SomeFlag2 = 0x0002,
  // etc...
}

Ensuite, le marshalling à l'adresse p/invoke est moins lisible en raison de la coulée nécessaire pour traduire l'énumé à la valeur appropriée.

Si c'était une variable de classe Const, une distribution ne serait pas nécessaire.

1
cmw

Les deux approches sont valides. Vous devriez choisir par cas.

Je peux ajouter que les opérations de bits de support Enums en tant que drapeaux (il y a même un attribut [Drapeaux] pour supporter cette sémantique et produire de jolies chaînes d'Enums).

Il y a un refactoring très similaire nommé remplaçant des énums avec le modèle de stratégie. Eh bien, dans votre cas, ce n'est pas tout à fait complet, car votre couleur est pasive et ne joue pas comme une stratégie. Cependant, pourquoi ne pas y penser comme un cas particulier?

1
Alex P

Yup, l'édition originale de "Java efficace" de Joshua Bloch, qui a été publiée avant Java 1.5 et support natif pour Enums en Java, a démontré le Typesafe Enum modèle. Malheureusement, la dernière version du livre cibles Java> 1.5, il utilise donc Java Enums à la place.

Deuxièmement, vous ne pouvez pas vous lancer de Int à Enum en Java.

Color nonsenseColor = (Color)17; // compile-error

Je ne peux pas parler pour d'autres langues.

0
organicveggie