web-dev-qa-db-fra.com

Enum "héritage"

J'ai un enum dans un espace de noms de bas niveau. J'aimerais fournir une classe ou une énumération dans un espace de noms de niveau intermédiaire qui "hérite" de l'énumération de niveau inférieur.

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 se substituer à l'énumération consommée, ce qui fournira une couche d'abstraction pour l'énum, ​​tout en laissant une instance de cette classe accéder à l'énum.

Pensées?

EDIT: L’une des raisons pour lesquelles je n’ai pas simplement choisi d’utiliser les consts dans les classes est que le bas niveau d’énumération est nécessaire à un service que je dois consommer. On m'a donné les WSDL et les XSD, qui définissent la structure comme une énumération. Le service ne peut pas être changé.

362
CodeMonkey1313

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

Voir la section 8.5.2 du spécification CLI pour plus de détails. Informations pertinentes de la spécification

  • Tous les énumérations doivent provenir de System.Enum
  • En raison de ce qui précède, tous les enums sont des types valeur et sont donc scellés
438
JaredPar

Vous pouvez réaliser 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;
}

Maintenant, vous pouvez utiliser ces classes de la même manière que lorsqu'elles étaient enum:

int i = Consume.B;

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

Si vous affectez les mêmes valeurs int aux constantes définies dans l'énumération existante, vous pouvez alors effectuer un transtypage entre l'énumération et les constantes, par exemple:

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;
158
M4N

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 très bien parce que 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);
    }
}

Pour croiser entre Base et Consume ...

Vous pouvez également définir les valeurs des enums comme des ints et les comparer comme des ints au lieu d’enum, mais ce genre de merde aussi.

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

108
Brian Genisio

Les solutions ci-dessus utilisant des classes avec des constantes int manquent de sécurité de type. C'est à dire. 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 l'une de ces classes en entrée.

Vous auriez besoin d'écrire

public void DoSomethingMeaningFull(int consumeValue) ...

Cependant, il existe une solution basée sur les classes de l'ancien temps de Java, quand il n'y avait pas d'énums disponibles. Cela fournit un comportement presque semblable à une énumération. Le seul inconvénient est que ces constantes ne peuvent pas être utilisées dans une instruction switch.

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) { /* ... */ }
    // ...
}
92
Seven

En ignorant le fait que base est un mot réservé, vous ne pouvez pas hériter d’enum.

La meilleure chose à 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());
   }
}

Comme ils sont tous du même type de base (c'est-à-dire: int), vous pouvez attribuer la valeur d'une instance d'un type à l'autre qui est convertie. Pas idéal mais ça marche.

13
Pascal

Je sais que cette réponse est un peu tardive mais voici ce que j'ai fini par faire:

public class BaseAnimal : IEquatable<BaseAnimal>
{
    public string Name { private set; get; }
    public int Value { private set; get; }

    public BaseAnimal(int value, String name)
    {
        this.Name = name;
        this.Value = value;
    }

    public override String ToString()
    {
        return Name;
    }

    public bool Equals(BaseAnimal other)
    {
        return other.Name == this.Name && other.Value == this.Value;
    }
}

public class AnimalType : BaseAnimal
{
    public static readonly BaseAnimal Invertebrate = new BaseAnimal(1, "Invertebrate");

    public static readonly BaseAnimal Amphibians = new BaseAnimal(2, "Amphibians");

    // etc        
}

public class DogType : AnimalType
{
    public static readonly BaseAnimal Golden_Retriever = new BaseAnimal(3, "Golden_Retriever");

    public static readonly BaseAnimal Great_Dane = new BaseAnimal(4, "Great_Dane");

    // etc        
}

alors je suis capable de faire des choses comme:

public void SomeMethod()
{
    var a = AnimalType.Amphibians;
    var b = AnimalType.Amphibians;

    if (a == b)
    {
        // should be equal
    }

    // call method as
    Foo(a);

    // using ifs
    if (a == AnimalType.Amphibians)
    {
    }
    else if (a == AnimalType.Invertebrate)
    {
    }
    else if (a == DogType.Golden_Retriever)
    {
    }
    // etc          
}

public void Foo(BaseAnimal typeOfAnimal)
{
}
6
Tono Nam

C'est ce que j'ai fait. Ce que j’ai fait différemment, c’est d’utiliser le même nom et le mot-clé new sur le mot "consumant" enum. Puisque le nom de la enum est le même, vous pouvez simplement l’utiliser inconsciemment et il aura raison. De plus, vous obtenez intellisense. Lors de la configuration, vous devez simplement veiller manuellement à ce que les valeurs soient copiées de la base et à ce qu'elles restent synchronisées. Vous pouvez aider cela avec les commentaires de code. C'est une autre raison pour laquelle, dans la base de données lors du stockage de enum valeurs, je stocke toujours la chaîne, pas la valeur. Parce que si vous utilisez des valeurs entières croissantes attribuées automatiquement, celles-ci peuvent changer avec le temps.

// Base Class for balls 
public class BaseBall
{
    // keep synced with subclasses!
    public enum Sizes
    {
        Small,
        Medium,
        Large
    }
}

public class VolleyBall : BaseBall
{
    // keep synced with base class!
    public new enum Sizes
    {
        Small = BaseBall.Sizes.Small,
        Medium = BaseBall.Sizes.Medium,
        Large = BaseBall.Sizes.Large,
        SmallMedium,
        MediumLarge,
        Ginormous
    }
}
4
toddmo

Les énumérations ne peuvent pas être dérivées d’autres énumérations, mais seulement d’int, de, court, long, long, ulong, byte et sbyte.

Comme Pascal l'a dit, vous pouvez utiliser les valeurs ou les constantes d'autres enum pour initialiser une valeur enum, mais c'est à peu près tout.

1
Jeroen Landheer

une autre solution possible:

public enum @base
{
    x,
    y,
    z
}

public enum consume
{
    x = @base.x,
    y = @base.y,
    z = @base.z,

    a,b,c
}

// TODO: Add a unit-test to check that if @base and consume are aligned

HTH

1
ShloEmi

Ce n'est pas possible (comme @JaredPar l'a déjà mentionné). Essayer de mettre la logique au travail est une mauvaise pratique. Si vous avez un base class qui a un enum, vous devez répertorier tous les enum-values possibles et la mise en œuvre de class doit fonctionner avec les valeurs qu'il connaît.

Par exemple. Supposons que vous ayez une classe de base BaseCatalog et qu'elle ait un enum ProductFormats (Digital, Physical). Vous pouvez alors avoir une MusicCatalog ou BookCatalog pouvant contenir à la fois les produits Digital et Physical, mais si la classe est ClothingCatalog, elle ne devrait contenir que Physical produits.

1
Jaider

Les énumérations ne sont pas des classes réelles, même si elles y ressemblent. En interne, ils sont traités comme leur type sous-jacent (par défaut, Int32). Par conséquent, vous ne pouvez le faire qu'en "copiant" des valeurs uniques d'une énumération à une autre et en les convertissant en nombre entier pour les comparer.

1
Lucero

Je voulais aussi surcharger Enums et créer un mélange de la réponse de 'Seven' sur cette page et la réponse de 'Merlyn Morgan-Graham' sur un message en double , plus quelques améliorations.
Principaux avantages de ma solution par rapport aux autres:

  • incrémentation automatique de la valeur int sous-jacente
  • dénomination automatique

Ceci est une solution prête à l'emploi et peut être directement insérée dans votre projet. Il est conçu pour mes besoins, donc si vous n'aimez pas certaines parties, remplacez-les simplement par votre propre code.

Premièrement, il y a la classe de base CEnum dont tous les énumérations personnalisées doivent hériter. Il a la fonctionnalité de base, semblable au type .net Enum:

public class CEnum
{
  protected static readonly int msc_iUpdateNames  = int.MinValue;
  protected static int          ms_iAutoValue     = -1;
  protected static List<int>    ms_listiValue     = new List<int>();

  public int Value
  {
    get;
    protected set;
  }

  public string Name
  {
    get;
    protected set;
  }

  protected CEnum ()
  {
    CommonConstructor (-1);
  }

  protected CEnum (int i_iValue)
  {
    CommonConstructor (i_iValue);
  }

  public static string[] GetNames (IList<CEnum> i_listoValue)
  {
    if (i_listoValue == null)
      return null;
    string[] asName = new string[i_listoValue.Count];
    for (int ixCnt = 0; ixCnt < asName.Length; ixCnt++)
      asName[ixCnt] = i_listoValue[ixCnt]?.Name;
    return asName;
  }

  public static CEnum[] GetValues ()
  {
    return new CEnum[0];
  }

  protected virtual void CommonConstructor (int i_iValue)
  {
    if (i_iValue == msc_iUpdateNames)
    {
      UpdateNames (this.GetType ());
      return;
    }
    else if (i_iValue > ms_iAutoValue)
      ms_iAutoValue = i_iValue;
    else
      i_iValue = ++ms_iAutoValue;

    if (ms_listiValue.Contains (i_iValue))
      throw new ArgumentException ("duplicate value " + i_iValue.ToString ());
    Value = i_iValue;
    ms_listiValue.Add (i_iValue);
  }

  private static void UpdateNames (Type i_oType)
  {
    if (i_oType == null)
      return;
    FieldInfo[] aoFieldInfo = i_oType.GetFields (BindingFlags.Public | BindingFlags.Static);

    foreach (FieldInfo oFieldInfo in aoFieldInfo)
    {
      CEnum oEnumResult = oFieldInfo.GetValue (null) as CEnum;
      if (oEnumResult == null)
        continue;
      oEnumResult.Name = oFieldInfo.Name;
    }
  }
}

Deuxièmement, voici 2 classes Enum dérivées. Toutes les classes dérivées ont besoin de méthodes de base pour fonctionner comme prévu. C'est toujours le même code standard; Je n'ai pas encore trouvé le moyen de l'externaliser vers la classe de base. Le code du premier niveau d'héritage diffère légèrement de tous les niveaux suivants.

public class CEnumResult : CEnum
{
  private   static List<CEnumResult>  ms_listoValue = new List<CEnumResult>();

  public    static readonly CEnumResult Nothing         = new CEnumResult (  0);
  public    static readonly CEnumResult SUCCESS         = new CEnumResult (  1);
  public    static readonly CEnumResult UserAbort       = new CEnumResult ( 11);
  public    static readonly CEnumResult InProgress      = new CEnumResult (101);
  public    static readonly CEnumResult Pausing         = new CEnumResult (201);
  private   static readonly CEnumResult Dummy           = new CEnumResult (msc_iUpdateNames);

  protected CEnumResult () : base ()
  {
  }

  protected CEnumResult (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> ();
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

public class CEnumResultClassCommon : CEnumResult
{
  private   static List<CEnumResultClassCommon> ms_listoValue = new List<CEnumResultClassCommon>();

  public    static readonly CEnumResult Error_InternalProgramming           = new CEnumResultClassCommon (1000);

  public    static readonly CEnumResult Error_Initialization                = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_ObjectNotInitialized          = new CEnumResultClassCommon ();
  public    static readonly CEnumResult Error_DLLMissing                    = new CEnumResultClassCommon ();
  // ... many more
  private   static readonly CEnumResult Dummy                               = new CEnumResultClassCommon (msc_iUpdateNames);

  protected CEnumResultClassCommon () : base ()
  {
  }

  protected CEnumResultClassCommon (int i_iValue) : base (i_iValue)
  {
  }

  protected override void CommonConstructor (int i_iValue)
  {
    base.CommonConstructor (i_iValue);

    if (i_iValue == msc_iUpdateNames)
      return;
    if (this.GetType () == System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType)
      ms_listoValue.Add (this);
  }

  public static new CEnumResult[] GetValues ()
  {
    List<CEnumResult> listoValue = new List<CEnumResult> (CEnumResult.GetValues ());
    listoValue.AddRange (ms_listoValue);
    return listoValue.ToArray ();
  }
}

Les classes ont été testées avec succès avec le code suivant:

private static void Main (string[] args)
{
  CEnumResult oEnumResult = CEnumResultClassCommon.Error_Initialization;
  string sName = oEnumResult.Name;   // sName = "Error_Initialization"

  CEnum[] aoEnumResult = CEnumResultClassCommon.GetValues ();   // aoEnumResult = {testCEnumResult.Program.CEnumResult[9]}
  string[] asEnumNames = CEnum.GetNames (aoEnumResult);
  int ixValue = Array.IndexOf (aoEnumResult, oEnumResult);    // ixValue = 6
}
0
Tobias Knauss