Enum "Vererbung"


391

Ich habe eine Aufzählung in einem niedrigen Namespace. Ich möchte eine Klasse oder 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 Klasse, die den Platz des Enum-Verbrauchs einnehmen kann, der 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 nicht nur auf Konstanten in Klassen umgestellt habe, ist, dass die Aufzählung auf niedriger Ebene von einem Dienst benötigt wird, den ich nutzen muss. Ich habe die WSDLs und die XSDs erhalten, die die Struktur als Aufzählung definieren. Der Dienst kann nicht geändert werden.


Antworten:


462

Das ist nicht möglich. Aufzählungen können nicht von anderen Aufzählungen erben. Tatsächlich müssen alle Aufzählungen tatsächlich von erben System.Enum. Mit C # kann die Syntax die zugrunde liegende Darstellung der Enum-Werte ändern, die wie Vererbung aussieht, in Wirklichkeit aber immer noch von System.enum erbt.

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

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

2
Und alle Werttypen stammen von System.ValueType
Raz Megrelidze

4
Ich muss erwähnen, dass die Antwort von @Seven eine legitime Problemumgehung
Tohid

Aber die Antwort von @ Steven kann unter bestimmten switchUmständen nicht verwendet werden.
Zionpi

@zionpi ja, aber beachte, dass (ich glaube) der Standard-Switch in denselben IL-Code kompiliert wird wie ein vollständiger if, else if, else-Block: was meiner Meinung nach sowieso eine bessere Syntax hat. Sie verlieren die Fähigkeit von Resharper / VS, alle Fallaussagen automatisch zu vervollständigen, aber ich denke, das ist nicht das Ende der Welt. Es ist eine persönliche Präferenz, aber ich bin kein Fan der switch-Anweisung.
MemeDeveloper

165

Mit Klassen können Sie 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;

Update (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;

8
Wie zählt man dann die Felder in dieser Klasse auf? Für mich ist das ein wichtiges Verhalten einer Aufzählung:Enum.GetValues(typeof(MyEnum)
Mike de Klerk

1
Sie können Reflexion verwenden: void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } }
Mnieto

2
Sie müssen sicherstellen, dass beim Sammeln von Eigenschaften mit Reflexion die geerbten Objekteigenschaften ignoriert werden.
Robert

Reflexion ist für diese Aufgabe super hässlich.
Dylanh724

1
Reflection muss nicht verwendet werden. Die Implementierung unter codeproject.com/Articles/20805/Enhancing-C-Enums ist eine gute Möglichkeit, da die Objekte erstellt werden, sie einer Liste hinzugefügt werden und diese Liste verwendet werden kann Gibt eine Liste der Objekttypen zurück. Wenn Sie es mit Vererbung mischen, müssen Sie sicherstellen, dass Sie die richtige Liste für die vererbenden Klassen verwenden.
PBo

112

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 Basis und Verbrauch zu wechseln ...

Sie können die Werte der Aufzählungen auch als Ints umwandeln und sie als Ints anstelle von Enum vergleichen, aber diese Art ist auch scheiße.

Die Rückgabe der Erweiterungsmethode sollte den Typ cast it type T geben.


4
Ich grabe das, Mann. Verwendete dieses Konzept, um einige Aufzählungen von meinem ORM zu meiner öffentlichen Schnittstelle (zu denen ohne ORM-Referenz) zu übertragen.
ObjectType

5
Man kann Enums zum Vergleich mit der Arbeit werfen:Base.A == (Base)Consume.A
Kresimir

1
Verwenden Sie (dezimal) Base.A == (dezimal) Consume.A. Grund: So funktioniert die Kombination von Bit-Flag und Maske (z. B. Beispiel in Enum.IsDefined msdn.microsoft.com/en-us/library/… ). Eine Aufzählung kann also auf eine Ganzzahl gesetzt werden, die in der Aufzählung nicht definiert ist. Verbrauchstest = 123456;
TamusJRoyce

3
@ TamusJRoyce dezimal? intwürde viel mehr Sinn machen. Wann würde eine Aufzählung einen Bruchteil haben!?!?!
ErikE

Dies stellt zumindest sicher, dass die entsprechenden Aufzählungskonstanten den gleichen ganzzahligen Wert haben. Aufzählungen sind schließlich nur eine Reihe von Ganzzahlkonstanten. C # erzwingt nicht, dass die zugewiesenen Werte gültige Aufzählungskonstanten sind, dh Aufzählungen sind im engeren Sinne nicht typsicher.
Olivier Jacot-Descombes

98

Die obigen Lösungen unter Verwendung von Klassen mit int-Konstanten sind nicht typsicher. Das heißt, Sie könnten neue Werte erfinden, die in der Klasse eigentlich nicht definiert sind. Darüber hinaus 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, als keine Aufzählungen verfügbar waren. Dies liefert ein fast enumartiges Verhalten. Die einzige Einschränkung 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) { /* ... */ }
    // ...
}

7
Ich denke, das ist die richtige Antwort. Sie können Enum nicht vererben, aber dies kann Ihnen ermöglichen, es zu verwalten!
Robob

11
Schön und sauber. +1. Nur ein Hinweis, Sie brauchen den int-Wert überhaupt nicht.
Ignacio Soler Garcia

1
Ich habe nie über Aufzählungen nachgedacht, als ob Sie FileOpenMode.Read> FileOpenMode.Write sagen könnten. Wenn dies der Fall ist, benötigen Sie für diese Aufzählung einen int oder noch besser einen IEqualityComparer. Grüße.
Ignacio Soler Garcia

3
@binki, das Zufallszahlen verwendet object, würde die Werte für jede Instanziierung der Assembly unterschiedlich machen, sodass sie nicht serialisiert werden können.
ivan_pozdeev

2
Damit können Sie MyEnum jedoch nicht "einschalten", oder? Ich meine, das ist hauptsächlich der Grund, warum ich Enums benutze ...
MemphiZ

13

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

Das Beste, was Sie tun können, ist so etwas:

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 denselben Basistyp haben (dh: int), können Sie den Wert einer Instanz eines Typs dem anderen zuweisen, den eine Besetzung enthält. Nicht ideal, aber es funktioniert.


2
Basis ist reserviert, Basis aber nicht
erikkallen

2
Er bezieht sich auf die Verwendung von base als Enum-Namen durch das OP, es ist nur ein Beispielname, da bin ich mir sicher
John Rasch

Gleich wie Genisios Antwort.
Nawfal

6

Ich weiß, dass diese Antwort etwas spät ist, aber das habe ich letztendlich 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 Dinge tun 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)
{
}

2
Magische Zahlen können gemäß stackoverflow.com/questions/757684/… durch Objekte ersetzt werden . In diesem speziellen Fall können Sie jedoch das Beste aus beiden Welten herausholen, indem Sie die Merkmale der vorhandenen biologischen Nomenklatur verwenden, um die Einzigartigkeit zu gewährleisten.
ivan_pozdeev

4

Das habe ich getan. Was ich anders gemacht habe, ist, den gleichen Namen und das newSchlüsselwort für das "Konsumieren" zu verwenden enum. Da der Name des enumgleich ist, können Sie ihn einfach sinnlos verwenden und es wird richtig sein. Außerdem bekommst du Intellisense. Sie müssen beim Einrichten nur manuell darauf achten, dass die Werte von der Basis kopiert und synchronisiert werden. Sie können dies zusammen mit Codekommentaren helfen. Dies ist ein weiterer Grund, warum enumich in der Datenbank beim Speichern von Werten immer die Zeichenfolge und nicht den Wert speichere. Denn wenn Sie automatisch zugewiesene ansteigende Ganzzahlwerte verwenden, können sich diese im Laufe 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
    }
}

2
Ziehen Sie in Betracht, einen anderen Bereich für die neuen Werte in der abgeleiteten Klasse festzulegen (z. B. SmallMedium = 100,), damit Sie die Kompatibilität mit älteren Versionen Ihrer Software beibehalten können, wenn Sie der Basisklasse neue Werte hinzufügen. Wenn Sie beispielsweise eine HugeGröße in Ihre Basisaufzählung aufnehmen, wird 4diese ihrem Wert zugewiesen , wird jedoch 4bereits von SmallMediumder abgeleiteten Klasse übernommen.
Roberto

1
@ Roberto, Um dies zu beheben, werde ich niemals Enum-Werte beibehalten, sondern nur die Namen. Und sie synchron zu halten, ist hier eine Voraussetzung. Das Hinzufügen Hugein der Basisklasse würde also ein Hugein der Unterklasse vorSmallMedium
toddmo

2
BaseBallist hier vielleicht nicht der klügste Name. Es ist verwirrend. Da ist ein Baseball eigentlich eine Sache. Wenn Sie vermissen, dass dies nur eine Basisklasse für Ball ist, sieht es sehr seltsam aus, dass ein Volleyball von einem physisch kleineren Baseball erbt :). Mein Vorschlag ist, nur zu verwendenBall
Nick N.

3

Alternative Lösung

In meiner Firma vermeiden wir es, über Projekte zu springen, um zu nicht üblichen Projekten auf niedrigerer Ebene zu gelangen. Beispielsweise kann unsere Präsentations- / API-Schicht nur auf unsere Domänenschicht verweisen, und die Domänenschicht kann nur auf die Datenschicht verweisen.

Dies ist jedoch ein Problem, wenn es Aufzählungen gibt, auf die sowohl von der Präsentations- als auch von der Domänenschicht verwiesen werden muss.

Hier ist die Lösung, die wir (bisher) implementiert haben. Es ist eine ziemlich gute Lösung und funktioniert gut für uns. Die anderen Antworten waren rundum zutreffend.

Die Grundvoraussetzung ist, dass Aufzählungen nicht vererbt werden können - Klassen jedoch. Damit...

// In the lower level project (or DLL)...
public abstract class BaseEnums
{
    public enum ImportanceType
    {
        None = 0,
        Success = 1,
        Warning = 2,
        Information = 3,
        Exclamation = 4
    }

    [Flags]
    public enum StatusType : Int32
    {
        None = 0,
        Pending = 1,
        Approved = 2,
        Canceled = 4,
        Accepted = (8 | Approved),
        Rejected = 16,
        Shipped = (32 | Accepted),
        Reconciled = (64 | Shipped)
    }

    public enum Conveyance
    {
        None = 0,
        Feet = 1,
        Automobile = 2,
        Bicycle = 3,
        Motorcycle = 4,
        TukTuk = 5,
        Horse = 6,
        Yak = 7,
        Segue = 8
    }

Dann, um die Aufzählungen in einem anderen übergeordneten Projekt zu "erben" ...

// Class in another project
public sealed class SubEnums: BaseEnums
{
   private SubEnums()
   {}
}

Das hat drei echte Vorteile ...

  1. Die Aufzählungsdefinitionen sind in beiden Projekten automatisch gleich - per Definition.
  2. Alle Änderungen an den Aufzählungsdefinitionen werden automatisch in der zweiten Klasse wiederholt, ohne dass Änderungen an der zweiten Klasse vorgenommen werden müssen.
  3. Die Aufzählungen basieren auf demselben Code, sodass die Werte leicht verglichen werden können (mit einigen Einschränkungen).

Um auf die Aufzählungen im ersten Projekt zu verweisen , können Sie das Präfix der Klasse verwenden: BaseEnums.StatusType.Pending oder ein "using static BaseEnums" hinzufügen. Aussage zu Ihren Nutzungen.

Im zweiten Projekt konnte ich jedoch beim Umgang mit der geerbten Klasse den Ansatz "using static ..." nicht zum Laufen bringen, sodass allen Verweisen auf die "geerbten Aufzählungen" die Klasse vorangestellt wurde, z. B. SubEnums.StatusType.Pending . Wenn jemand eine Möglichkeit findet, die Verwendung des statischen Ansatzes im zweiten Projekt zuzulassen , lassen Sie es mich wissen.

Ich bin sicher, dass dies optimiert werden kann, um es noch besser zu machen - aber das funktioniert tatsächlich und ich habe diesen Ansatz in Arbeitsprojekten verwendet.

Bitte stimmen Sie dies ab, wenn Sie es hilfreich finden.


2

Ich wollte auch Enums überladen und erstellte eine Mischung aus der Antwort von 'Seven' auf dieser Seite und der Antwort von 'Merlyn Morgan-Graham' auf einem doppelten Beitrag davon sowie ein paar Verbesserungen.
Hauptvorteile meiner Lösung gegenüber den anderen:

  • automatische Inkrementierung des zugrunde liegenden int-Werts
  • automatische Benennung

Dies ist eine sofort einsatzbereite Lösung, die direkt in Ihr Projekt eingefügt werden kann. 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 Aufzählungen erben sollen. Es hat die Grundfunktionalität, ähnlich dem .net- EnumTyp:

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 arbeiten. Es ist immer der gleiche Boilerplate-Code. Ich habe noch keinen Weg gefunden, es an die Basisklasse auszulagern. Der Code der ersten Vererbungsstufe 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
}

1

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 in ihre Ganzzahl umwandeln, um sie auf Gleichheit zu vergleichen.


1

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 Aufzählungen verwenden, um einen Aufzählungswert zu initialisieren, aber das war es auch schon.


8
Aufgrund der c # -Syntax ist es eine Fehlbezeichnung, aber Enums können nicht von int, uint usw. erben. Unter der Haube erben sie immer noch von System.Enum. Es ist nur so, dass das Mitglied, das die Aufzählung darstellt, in int, uint usw. eingegeben wird.
JaredPar

@ JarPar. Wenn eine Aufzählung von einem uint abgeleitet wird, bedeutet dies, dass alle Werte uint usw. sind. Standardmäßig erbt eine Aufzählung int. (Schauen Sie sich die C # -Spezifikation an, enum SomeEnum: uint {...} funktioniert tatsächlich.)
Jeroen Landheer

4
Nicht wirklich. Es erbt System.enum. Wie bereits erwähnt und hier öfter, ist das, was Sie für Vererbung halten, nur eine sprachliche Dusambiguität in csharp.
TomTom

1

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

Dies ist nicht möglich (wie bereits erwähnt bei @JaredPar). Der Versuch, die Logik zu umgehen, ist eine schlechte Praxis. Wenn Sie eine base classhaben enum, die eine haben , sollten Sie alle möglichen auflistenenum-values dort auflisten, und die Implementierung der Klasse sollte mit den Werten arbeiten, die sie kennt.

Angenommen, Sie haben eine Basisklasse BaseCatalogund sie hat ein enum ProductFormats( Digital, Physical). Dann können Sie ein MusicCatalogoder haben BookCatalog, das sowohl Digitalals auch PhysicalProdukte ClothingCatalogenthält Physical. Wenn die Klasse jedoch ist , sollte sie nur Produkte enthalten.


1

Mir ist klar, dass ich etwas spät zu dieser Party komme, aber hier sind meine zwei Cent.

Wir sind uns alle darüber im Klaren, dass die Enum-Vererbung vom Framework nicht unterstützt wird. In diesem Thread wurden einige sehr interessante Problemumgehungen vorgeschlagen, aber keine davon fühlte sich so an, wie ich gesucht hatte, also habe ich es selbst ausprobiert.

Wir stellen vor: ObjectEnum

Sie können den Code und die Dokumentation hier überprüfen: https://github.com/dimi3tron/ObjectEnum .

Und das Paket hier: https://www.nuget.org/packages/ObjectEnum

Oder installieren Sie es einfach: Install-Package ObjectEnum

Zusamenfassend, ObjectEnum<TEnum> fungiert als Wrapper für jede Aufzählung. Durch Überschreiben von GetDefinedValues ​​() in Unterklassen kann angegeben werden, welche Aufzählungswerte für diese bestimmte Klasse gültig sind.

Eine Anzahl von Bedienerüberladungen wurde hinzugefügt, um eine ObjectEnum<TEnum> Instanz so verhält, als wäre sie eine Instanz der zugrunde liegenden Aufzählung, wobei die definierten Werteinschränkungen berücksichtigt werden. Dies bedeutet, dass Sie die Instanz einfach mit einem int- oder enum-Wert vergleichen und somit in einem Switch-Fall oder einer anderen Bedingung verwenden können.

Ich möchte auf das oben erwähnte Github-Repo für Beispiele und weitere Informationen verweisen.

Ich hoffe, Sie finden das nützlich. Fühlen Sie sich frei zu kommentieren oder ein Problem auf Github für weitere Gedanken oder Kommentare zu öffnen.

Hier sind einige kurze Beispiele dafür, was Sie tun können ObjectEnum<TEnum>:

var sunday = new WorkDay(DayOfWeek.Sunday); //throws exception
var monday = new WorkDay(DayOfWeek.Monday); //works fine
var label = $"{monday} is day {(int)monday}." //produces: "Monday is day 1."
var mondayIsAlwaysMonday = monday == DayOfWeek.Monday; //true, sorry...

var friday = new WorkDay(DayOfWeek.Friday);

switch((DayOfWeek)friday){
    case DayOfWeek.Monday:
        //do something monday related
        break;
        /*...*/
    case DayOfWeek.Friday:
        //do something friday related
        break;
}

-8

Sie können die Vererbung in enum durchführen, sie ist jedoch nur auf folgende Typen beschränkt. int, uint, byte, sbyte, kurz, kurz, lang, ulong

Z.B

public enum Car:int{
Toyota,
Benz,
}

2
Ich denke, das OP hat darum gebeten, eine Aufzählung von einer anderen zu erben, nicht nur von einem numerischen Basistyp (was alle Aufzählungen in C # implizit oder explizit tun)
reirab
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.