wake-up-neo.com

Enum "Vererbung"

Ich habe eine Enumeration in einem Namespace niedriger Ebene. Ich möchte eine Klasse oder eine Aufzählung in einem Namespace mittlerer Ebene bereitstellen, die die Aufzählung niedriger Ebene "erbt".

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

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

Ich hoffe, dass dies möglich ist, oder vielleicht eine Art von Klasse, die den Platz des Enum-Consumes einnimmt und eine Abstraktionsebene für das Enum bereitstellt, aber dennoch eine Instanz dieser Klasse auf das Enum zugreifen lässt.

Gedanken?

BEARBEITEN: Einer der Gründe, warum ich dies in Klassen nicht einfach auf consts umgestellt habe, ist, dass ein Service, den ich in Anspruch nehmen muss, die niedrige Enumeration benötigt. Ich habe die WSDLs und die XSDs erhalten, die die Struktur als Enumeration definieren. Der Dienst kann nicht geändert werden.

362
CodeMonkey1313

Das ist nicht möglich. Enums können nicht von anderen Enums erben. Tatsächlich müssen alle Aufzählungen tatsächlich von System.Enum Erben. Mit C # kann die Syntax die zugrunde liegende Darstellung der Aufzählungswerte ändern, die wie eine Vererbung aussehen, aber tatsächlich immer noch von System.enum erben.

Ausführliche Informationen finden Sie in Abschnitt 8.5.2 der CLI-Spezifikation . Relevante Informationen aus der Spezifikation

  • Alle Aufzählungen müssen von System.Enum Stammen.
  • Aus diesem Grund sind alle Aufzählungen Werttypen und daher versiegelt
438
JaredPar

Sie können mit dem Unterricht erreichen, was Sie wollen:

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;
}

Jetzt können Sie diese Klassen ähnlich wie bei Aufzählungen verwenden:

int i = Consume.B;

pdate (nach dem Update der Frage):

Wenn Sie den Konstanten dieselben int-Werte zuweisen, wie sie in der vorhandenen Aufzählung definiert sind, können Sie zwischen der Aufzählung und den Konstanten umwandeln, z.

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

Die kurze Antwort lautet nein. Sie können ein bisschen spielen, wenn Sie wollen:

Sie können immer so etwas tun:

private enum Base
{
    A,
    B,
    C
}

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

Aber es funktioniert nicht so gut, weil Base.A! = Consume.A

Sie können jedoch immer so etwas tun:

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);
    }
}

Um zwischen Base und Consume zu wechseln ...

Sie können die Werte der Aufzählungen auch in Ints umwandeln und sie als Ints statt als Enum vergleichen.

Die Rückgabe der Erweiterungsmethode sollte cast it type T sein.

108
Brian Genisio

Die obigen Lösungen, die Klassen mit int-Konstanten verwenden, haben keine Typensicherheit. Das heißt Sie könnten neue Werte erfinden, die tatsächlich nicht in der Klasse definiert sind. Außerdem ist es beispielsweise nicht möglich, eine Methode zu schreiben, die eine dieser Klassen als Eingabe verwendet.

Sie müssten schreiben

public void DoSomethingMeaningFull(int consumeValue) ...

Es gibt jedoch eine klassenbasierte Lösung der alten Java-Zeiten, in denen keine Aufzählungen verfügbar waren. Dies führt zu einem fast enumartigen Verhalten. Der einzige Nachteil ist, dass diese Konstanten nicht in einer switch-Anweisung verwendet werden können.

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

Wenn Sie die Tatsache ignorieren, dass die Basis ein reserviertes Wort ist, können Sie die Aufzählung nicht vererben.

Das Beste, was Sie tun können, ist Folgendes:

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());
   }
}

Da sie alle den gleichen Basistyp haben (dh: int), können Sie den Wert einer Instanz eines Typs der anderen zuweisen, die eine Umwandlung durchführt. Nicht ideal, aber es funktioniert.

13
Pascal

Ich weiß, dass diese Antwort etwas spät ist, aber am Ende habe ich Folgendes getan:

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        
}

Dann kann ich Sachen machen wie:

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

Das habe ich getan. Was ich anders gemacht habe, ist, den gleichen Namen und das new-Schlüsselwort für das "verbrauchende" enum zu verwenden. Da der Name von enum derselbe ist, können Sie ihn einfach sinnlos verwenden, und er wird richtig sein. Außerdem bekommst du Intellisense. Sie müssen nur manuell darauf achten, dass die Werte von der Basis kopiert und synchronisiert werden. Sie können das zusammen mit Code-Kommentaren helfen. Dies ist ein weiterer Grund, warum ich in der Datenbank beim Speichern von enum Werten immer die Zeichenfolge und nicht den Wert speichere. Denn wenn Sie automatisch steigende Integer-Werte vergeben, können sich diese mit der Zeit ändern.

// 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

Aufzählungen können nicht von anderen Aufzählungen abgeleitet werden, sondern nur von int, uint, short, ushort, long, ulong, byte und sbyte.

Wie Pascal sagte, können Sie die Werte oder Konstanten anderer Enumerationen verwenden, um einen Enumerationswert zu initialisieren, aber das war es auch schon.

1
Jeroen Landheer

eine andere mögliche Lösung:

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

Dies ist nicht möglich (wie @JaredPar bereits erwähnt). Der Versuch, Logik zu verwenden, um dies zu umgehen, ist eine schlechte Praxis. Falls Sie einen base class Mit einem enum haben, sollten Sie dort alle möglichen enum-values Auflisten, und die Implementierung der Klasse sollte mit den ihr bekannten Werten funktionieren.

Z.B. Angenommen, Sie haben eine Basisklasse BaseCatalog und sie hat einen enum ProductFormats (Digital, Physical). Dann können Sie ein MusicCatalog oder BookCatalog haben, das sowohl Digital als auch Physical Produkte enthalten kann, aber wenn die Klasse ClothingCatalog ist, ist es sollte nur Physical Produkte enthalten.

1
Jaider

Aufzählungen sind keine tatsächlichen Klassen, auch wenn sie so aussehen. Intern werden sie genauso behandelt wie ihr zugrunde liegender Typ (standardmäßig Int32). Daher können Sie dies nur tun, indem Sie einzelne Werte von einer Aufzählung in eine andere "kopieren" und sie in ihre Ganzzahl umwandeln, um sie auf Gleichheit zu vergleichen.

1
Lucero

Ich wollte auch Enums überladen und habe eine Mischung aus die Antwort von 'Seven' auf dieser Seite und die Antwort von 'Merlyn Morgan-Graham' auf einen doppelten Beitrag von diesem erstellt , plus ein paar Verbesserungen.
Hauptvorteile meiner Lösung gegenüber den anderen:

  • automatisches Inkrementieren des zugrunde liegenden int-Werts
  • automatische Benennung

Dies ist eine Standardlösung und kann direkt in Ihr Projekt eingefügt werden. Es ist auf meine Bedürfnisse zugeschnitten. Wenn Ihnen einige Teile davon nicht gefallen, ersetzen Sie sie einfach durch Ihren eigenen Code.

Erstens gibt es die Basisklasse CEnum, von der alle benutzerdefinierten Enums erben sollen. Es verfügt über die grundlegende Funktionalität, ähnlich dem Typ .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;
    }
  }
}

Zweitens sind hier 2 abgeleitete Enum-Klassen. Alle abgeleiteten Klassen benötigen einige grundlegende Methoden, um wie erwartet zu funktionieren. Es ist immer derselbe Kesselcode. Ich habe noch keinen Weg gefunden, es an die Basisklasse auszulagern. Der Code der ersten Vererbungsebene unterscheidet sich geringfügig von allen nachfolgenden Ebenen.

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 ();
  }
}

Die Klassen wurden erfolgreich mit folgendem Code getestet:

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