486 votes

Enum "Héritage

J'ai un enum dans un espace de nom de bas niveau. J'aimerais fournir une classe ou une somme dans un espace de noms de niveau intermédiaire qui "hérite" de la somme de bas niveau.

namespace low
{
   public enum base
   {
      x, y, z
   }
}

namespace mid
{
   public enum consume : low.base
   {
   }
}

J'espère que cela est possible, ou peut-être une sorte de classe qui peut prendre la place de l'enum consommer qui fournira une couche d'abstraction pour l'enum, mais encore laisser une instance de cette classe accéder à l'enum.

Qu'en pensez-vous ?

EDIT : L'une des raisons pour lesquelles je ne suis pas passé aux constantes dans les classes est que l'enum de bas niveau est nécessaire à un service que je dois consommer. On m'a donné les WSDLs et les XSDs, qui définissent la structure comme un enum. Le service ne peut pas être modifié.

1 votes

1 votes

Vous pouvez utiliser un Int32 et le convertir en votre enum si nécessaire.

539voto

JaredPar Points 333733

Ce n'est pas possible. Les enums ne peuvent pas hériter d'autres enums. En fait, tous les enums doivent hériter de System.Enum. C# autorise la syntaxe permettant de modifier la représentation sous-jacente des valeurs des enums, ce qui ressemble à de l'héritage, mais en réalité, elles héritent toujours de System.enum.

Voir la section 8.5.2 de la CLI spec pour obtenir tous les détails. Informations pertinentes tirées du cahier des charges

  • Tous les enums doivent dériver de System.Enum.
  • En raison de ce qui précède, tous les enums sont des types de valeurs et sont donc scellés.

2 votes

Et tous les types de valeurs dérivent de System.ValueType

4 votes

Je dois mentionner que la réponse de @Seven est une solution de rechange légitime : stackoverflow.com/a/4042826/538387

0 votes

Mais la réponse de @Steven ne peut pas être utilisée sous switch situation.

179voto

M4N Points 48758

Vous pouvez obtenir ce que vous voulez avec des cours :

public class Base
{
    public const int A = 1;
    public const int B = 2;
    public const int C = 3;
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

Vous pouvez maintenant utiliser ces classes de la même manière que lorsqu'elles étaient des enums :

int i = Consume.B;

Mise à jour (après votre mise à jour de la question) :

Si vous attribuez aux constantes les mêmes valeurs int que celles définies dans l'énumération existante, vous pouvez alors effectuer un casting entre l'énumération et les constantes, par ex :

public enum SomeEnum // this is the existing enum (from WSDL)
{
    A = 1,
    B = 2,
    ...
}
public class Base
{
    public const int A = (int)SomeEnum.A;
    //...
}
public class Consume : Base
{
    public const int D = 4;
    public const int E = 5;
}

// where you have to use the enum, use a cast:
SomeEnum e = (SomeEnum)Consume.B;

14 votes

Comment énumérer les champs de cette classe alors ? Pour moi, c'est le comportement important d'un enum : Enum.GetValues(typeof(MyEnum)

1 votes

Vous pouvez utiliser la réflexion : void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } }

2 votes

Vous devez vous assurer que la collecte des propriétés par réflexion ignore les propriétés d'objet héritées.

142voto

Brian Genisio Points 30777

La réponse courte est non. Vous pouvez jouer un peu, si vous voulez :

Vous pouvez toujours faire quelque chose comme ça :

private enum Base
{
    A,
    B,
    C
}

private enum Consume
{
    A = Base.A,
    B = Base.B,
    C = Base.C,
    D,
    E
}

Mais, cela ne fonctionne pas si bien que ça car Base.A != Consume.A

Vous pouvez toujours faire quelque chose comme ça, cependant :

public static class Extensions
{
    public static T As<T>(this Consume c) where T : struct
    {
        return (T)System.Enum.Parse(typeof(T), c.ToString(), false);
    }
}

Afin de croiser la Base et la Consommation...

Vous pourriez également convertir les valeurs des enums en ints, et les comparer en ints au lieu d'enum, mais ça ne marche pas non plus.

Le retour de la méthode d'extension doit être de type T.

4 votes

J'adore ça, mec. J'ai utilisé ce concept pour faire passer quelques enums de mon ORM à mon interface publique (pour ceux qui n'ont pas la référence ORM).

9 votes

On peut couler les enums pour que la comparaison fonctionne : Base.A == (Base)Consume.A

1 votes

Utiliser (décimal)Base.A == (décimal)Consume.A. Raison : C'est ainsi que la combinaison drapeau/ masque de bits fonctionne (par exemple, l'exemple dans Enum.IsDefined msdn.microsoft.com/fr/us/library/ ). Ainsi, un enum peut être réglé sur un nombre entier non défini dans l'enum. Consommer test = 123456 ;

111voto

Seven Points 1604

Les solutions ci-dessus utilisant des classes avec des constantes int manquent de sécurité de type. C'est-à-dire que vous pourriez inventer de nouvelles valeurs qui ne sont pas définies dans la classe. De plus, il n'est pas possible par exemple d'écrire une méthode prenant une de ces classes comme entrée.

Vous devez écrire

public void DoSomethingMeaningFull(int consumeValue) ...

Cependant, il existe une solution basée sur les classes de l'ancien temps de Java, lorsqu'il n'y avait pas d'enums disponibles. Cette solution offre un comportement presque similaire à celui des enums. Le seul problème est que ces constantes ne peuvent pas être utilisées dans une déclaration de commutation.

public class MyBaseEnum
{
    public static readonly MyBaseEnum A = new MyBaseEnum( 1 );
    public static readonly MyBaseEnum B = new MyBaseEnum( 2 );
    public static readonly MyBaseEnum C = new MyBaseEnum( 3 );

    public int InternalValue { get; protected set; }

    protected MyBaseEnum( int internalValue )
    {
        this.InternalValue = internalValue;
    }
}

public class MyEnum : MyBaseEnum
{
    public static readonly MyEnum D = new MyEnum( 4 );
    public static readonly MyEnum E = new MyEnum( 5 );

    protected MyEnum( int internalValue ) : base( internalValue )
    {
        // Nothing
    }
}

[TestMethod]
public void EnumTest()
{
    this.DoSomethingMeaningful( MyEnum.A );
}

private void DoSomethingMeaningful( MyBaseEnum enumValue )
{
    // ...
    if( enumValue == MyEnum.A ) { /* ... */ }
    else if (enumValue == MyEnum.B) { /* ... */ }
    // ...
}

7 votes

Je pense que c'est la bonne réponse. Vous ne pouvez pas avoir d'héritage pour les Enum mais cela peut vous permettre de les gérer !

12 votes

Sympathique et propre. +1. Juste un conseil, vous n'avez vraiment pas besoin de la valeur int du tout.

0 votes

@SoMoS, suggérez-vous que object être utilisé comme recommandé pour lock ? Je n'arrive pas à trouver un moyen de rendre cette idée compatible avec la partie "numération" de l'article. enumMyEnum.First < MyEnum.Second . Mais si passer commande n'est pas important pour votre enum alors ce serait un moyen astucieux d'éviter les constantes int magiques.

15voto

Pascal Points 83

En ignorant le fait que base est un mot réservé, vous ne pouvez pas faire l'héritage de enum.

La meilleure chose que tu puisses faire est quelque chose comme ça :

public enum Baseenum
{
   x, y, z
}

public enum Consume
{
   x = Baseenum.x,
   y = Baseenum.y,
   z = Baseenum.z
}

public void Test()
{
   Baseenum a = Baseenum.x;
   Consume newA = (Consume) a;

   if ((Int32) a == (Int32) newA)
   {
   MessageBox.Show(newA.ToString());
   }
}

Puisqu'ils sont tous du même type de base (c'est-à-dire int), vous pouvez assigner la valeur d'une instance d'un type à l'autre par un cast. Ce n'est pas idéal mais ça marche.

2 votes

La base est réservée mais la base ne l'est pas

2 votes

Il fait référence à l'utilisation par le PO de base comme nom d'enum, c'est juste un exemple de nom, j'en suis sûr.

0 votes

Même réponse que celle de Genisio.

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