96 votes

Extension d'un enum via l'héritage

Je sais que cela va plutôt à l'encontre de l'idée des enums, mais est-il possible d'étendre les enums en C#/Java ? Je veux dire "étendre" dans le sens d'ajouter de nouvelles valeurs à un enum, mais aussi dans le sens OO d'hériter d'un enum existant.

Je suppose que ce n'est pas possible en Java, puisqu'il n'y en a eu que récemment (Java 5 ?). C# semble plus indulgent envers les personnes qui veulent faire des choses folles, alors j'ai pensé que cela pourrait être possible d'une manière ou d'une autre. Je suppose qu'il est possible d'utiliser la réflexion (même si vous n'utilisez jamais cette méthode) ?

Je ne suis pas nécessairement intéressé par la mise en œuvre d'une méthode donnée, elle a juste suscité ma curiosité lorsqu'elle m'est venue à l'esprit :-)

0 votes

Cela répond-il à votre question ? Enum "Héritage

111voto

Rik Points 12802

La raison pour laquelle vous ne pouvez pas étendre les Enums est que cela entraînerait des problèmes de polymorphisme.

Disons que vous avez un enum MyEnum avec les valeurs A, B et C, et que vous l'étendez avec la valeur D en tant que MyExtEnum.

Supposons qu'une méthode attende une valeur myEnum quelque part, par exemple en tant que paramètre. Il devrait être légal de fournir une valeur MyExtEnum, car il s'agit d'un sous-type, mais que ferez-vous s'il s'avère que la valeur est D ?

Pour éliminer ce problème, l'extension des enums est illégale.

4 votes

En fait, la raison est simplement que cela n'a aucun sens. Le problème que vous mentionnez, à savoir que le code client reçoit une constante qu'il n'attend pas, existe toujours avec l'implémentation actuelle - en fait le compilateur ne vous laisse pas switch sur des valeurs d'énumération sans fournir un default ou de lancer une exception. Et, même si c'était le cas, l'enum au moment de l'exécution pourrait provenir d'une compilation séparée.

0 votes

@Raffaele Je viens juste d'essayer, et au moins dans Java 7, vous pouvez activer un enum sans l'option default le cas ou le lancer.

0 votes

@Dathan vous avez tout à fait raison ! Tel qu'il est écrit, c'est tout simplement faux - je devrais peut-être le supprimer ? Je pensais au cas où vous utiliseriez un fichier switch pour fournir une valeur requise, comme l'initiale d'un local, ou pour return

45voto

Travis Points 6062

Lorsque les enums intégrés ne suffisent pas, vous pouvez le faire à l'ancienne et créer vos propres enums. Par exemple, si vous souhaitez ajouter une propriété supplémentaire, par exemple un champ de description, vous pouvez procéder comme suit :

public class Action {
    public string Name {get; private set;}
    public string Description {get; private set;}

    private Action(string name, string description) {
        Name = name;
        Description = description;
    }

    public static Action DoIt = new Action("Do it", "This does things");
    public static Action StopIt = new Action("Stop It", "This stops things");
}

Vous pouvez alors le traiter comme un enum comme suit :

public void ProcessAction(Action a) {
    Console.WriteLine("Performing action: " + a.Name)
    if (a == Action.DoIt) {
       // ... and so on
    }
}

L'astuce consiste à s'assurer que le constructeur est privé (ou protégé si vous voulez hériter), et que vos instances sont statiques.

0 votes

Je ne suis pas un spécialiste du C#, mais ne voulez-vous pas un peu de final (ou sealed/const/quelque chose) ?

1 votes

Je ne le crois pas. Ou, du moins, pas dans la situation où vous voudriez hériter de cette classe pour ajouter de nouvelles valeurs "enum". Final et sealed empêchent l'héritage, je crois.

4 votes

@alastairs Je pense qu'il voulait dire ajouter final à la public static Action DoIt = new Action("Do it", "This does things"); ligne, plutôt que la classe.

43voto

Ken Points 229

Vous allez dans le mauvais sens : une sous-classe d'un enum aurait moins de entrées.

En pseudo-code, pensez :

enum Animal { Mosquito, Dog, Cat };
enum Mammal : Animal { Dog, Cat };  // (not valid C#)

Toute méthode qui peut accepter un Animal devrait pouvoir accepter un Mammifère, mais pas l'inverse. La sous-classification sert à rendre quelque chose plus spécifique, pas plus général. C'est pourquoi "objet" est la racine de la hiérarchie des classes. De même, si les enums étaient héritables, alors une hypothétique racine de la hiérarchie des enums aurait tous les symboles possibles.

Mais non, C#/Java n'autorisent pas les sous-enums, AFAICT, bien que cela soit parfois très utile. C'est probablement parce qu'ils ont choisi d'implémenter les Enums comme des ints (comme le C) au lieu de symboles internés (comme le Lisp). (Ci-dessus, que représente (Animal)1, et que représente (Mammifère)1, et s'agit-il de la même valeur).

Vous pourriez écrire votre propre classe de type enum (avec un nom différent) qui fournit cela, cependant. Avec les attributs C#, cela pourrait même avoir l'air sympa.

3 votes

Analyse intéressante. je n'y avais jamais pensé de cette façon !

1 votes

Intéressant, mais je pense que c'est faux. Laisser entendre qu'il devrait y avoir moins de types dans une sous-classe a du sens en termes d'arbres de classification des animaux, mais pas en termes de code. Quand on fait une sous-classe dans le code, il n'y a jamais moins de méthodes. Il n'y a jamais moins de variables membres. Votre argument ne semble pas s'appliquer aux logiciels comme il s'applique aux classifications animales.

5 votes

@Kieveli, votre analyse est erronée. L'ajout d'un membre n'a pas le même effet sur un objet que l'ajout à l'ensemble de ses valeurs possibles. Ce n'est pas quelque chose que Ken a inventé ; il existe des règles claires et précises en informatique qui expliquent pourquoi cela fonctionne de cette façon. Essayez de chercher covariance et contravariance (ce n'est pas une simple masturbation académique, cela vous aidera à comprendre les règles pour des choses comme les signatures de fonctions et les conteneurs).

13voto

Les Enums sont censés représenter l'énumération de toutes les valeurs possibles, donc l'extension va plutôt à l'encontre de cette idée.

Cependant, ce que vous pouvez faire en Java (et probablement en C++0x) est d'avoir une interface au lieu d'une classe d'enum. Ensuite, mettez vos valeurs standard dans un enum qui implémente la fonctionnalité. Évidemment, vous ne pouvez pas utiliser java.util.EnumSet et autres. C'est l'approche adoptée dans "more NIO features", qui devrait être dans le JDK7.

public interface Result {
    String name();
    String toString();
}
public enum StandardResults implements Result {
    TRUE, FALSE
}

public enum WTFResults implements Result {
    FILE_NOT_FOUND
}

4voto

McKenzieG1 Points 5294

Vous pouvez utiliser la réflexion .NET pour récupérer les étiquettes et les valeurs d'un enum existant au moment de l'exécution ( Enum.GetNames() et Enum.GetValues() sont les deux méthodes spécifiques à utiliser), puis utiliser l'injection de code pour en créer un nouveau avec ces éléments et de nouveaux. Cela semble quelque peu analogue à "hériter d'un enum existant".

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