Enum „Dziedziczenie”

391

Mam wyliczenie w przestrzeni nazw niskiego poziomu. Chciałbym zapewnić klasę lub wyliczenie w przestrzeni nazw średniego poziomu, która „dziedziczy” wyliczenie niskiego poziomu.

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

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

Mam nadzieję, że jest to możliwe, a może jakaś klasa, która może zająć miejsce enum konsumpcji, która zapewni warstwę abstrakcji dla enum, ale nadal pozwoli instancji tej klasy uzyskać dostęp do enum.

Myśli?

EDYCJA: Jednym z powodów, dla których nie zmieniłem tego na consts w klasach, jest to, że wyliczenie niskiego poziomu jest potrzebne przez usługę, którą muszę wykorzystać. Dostałem WSDL i XSD, które definiują strukturę jako wyliczenie. Usługi nie można zmienić.

CodeMonkey1313
źródło
1
Jedną z opcji jest codeproject.com/Articles/20805/Enhancing-C-Enums
Walter Vehoeven

Odpowiedzi:

462

To jest niemożliwe. Wyliczenia nie mogą dziedziczyć od innych wyliczeń. W rzeczywistości wszystkie wyliczenia muszą faktycznie dziedziczyć System.Enum. C # pozwala składni na zmianę reprezentacji wartości wyliczenia, która wygląda jak dziedziczenie, ale w rzeczywistości nadal dziedziczą one z System.enum.

Szczegółowe informacje można znaleźć w sekcji 8.5.2 specyfikacji CLI . Istotne informacje ze specyfikacji

  • Wszystkie wyliczenia muszą pochodzić System.Enum
  • Z tego powodu wszystkie wyliczenia są typami wartości i dlatego są zapieczętowane
JaredPar
źródło
2
Wszystkie typy wartości pochodzą od System.ValueType
Raz Megrelidze
4
Muszę wspomnieć, że odpowiedź @Seven jest uzasadnionym obejściem: stackoverflow.com/a/4042826/538387
Tohid
ale odpowiedzi @ Stevena nie można użyć w switchsytuacji.
zionpi,
@zionpi tak, ale zauważ, że (uważam) standardowy przełącznik zostaje skompilowany do tego samego kodu IL jako pełnego, jeśli, inaczej jeśli, inaczej blok zrobiłby: co moim zdaniem i tak ma lepszą składnię. Masz luźną zdolność resharpera / VS do autouzupełniania wszystkich instrukcji, ale myślę, że to nie koniec świata. To osobiste preferencje, ale nie jestem fanem oświadczenia o zmianie.
MemeDeveloper
165

Możesz osiągnąć to, co chcesz dzięki zajęciom:

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

Teraz możesz używać tych klas podobnie jak wtedy, gdy były one wyliczeniami:

int i = Consume.B;

Aktualizacja (po aktualizacji pytania):

Jeśli przypiszesz te same wartości int do stałych zdefiniowanych w istniejącym wyliczeniu, możesz rzutować między wyliczeniem i stałymi, np .:

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;
M4N
źródło
8
Jak zatem wyliczyć pola w tej klasie? Dla mnie to ważne zachowanie wyliczenia:Enum.GetValues(typeof(MyEnum)
Mike de Klerk
1
Możesz użyć refleksji: void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } }
mnieto
2
Musisz upewnić się, że gromadzenie właściwości z odbiciem ignoruje odziedziczone właściwości Object.
Robert,
Odbicie jest bardzo brzydkie do tego zadania.
dylanh724
1
Nie ma potrzeby korzystania z Reflection, implementacja na codeproject.com/Articles/20805/Enhancing-C-Enums to dobry sposób, aby to zrobić, ponieważ obiekty są tworzone, są dodawane do listy, a tej listy można użyć do zwraca listę typów obiektów. Kiedy połączysz go z dziedziczeniem, będziesz musiał upewnić się, że używasz odpowiedniej listy dla klas dziedziczących.
PBo
112

Krótka odpowiedź brzmi: nie. Możesz trochę zagrać, jeśli chcesz:

Zawsze możesz zrobić coś takiego:

private enum Base
{
    A,
    B,
    C
}

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

Ale to nie działa tak dobrze, ponieważ Base.A! = Consume.A

Zawsze możesz jednak zrobić coś takiego:

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

Aby przejść między bazą a konsumpcją ...

Możesz także rzucić wartości wyliczeń jako ints i porównać je jako ints zamiast enum, ale to też jest do bani.

Zwracana metoda rozszerzenia powinna mieć typ cast to T.

Brian Genisio
źródło
4
Kopie to, stary. Użyłem tej koncepcji, aby przenieść niektóre wyliczenia z mojej ORM do mojego publicznego interfejsu (do tych bez odniesienia do ORM).
ObjectType
5
Można porównywać wyliczenia do pracy:Base.A == (Base)Consume.A
Kresimir
1
Użyj (dziesiętnie) Podstawa. A == (dziesiętnie) Zużycie. Powód: Tak działa łączenie flagi bitowej / maski (np. W Enum.IsDefined msdn.microsoft.com/en-us/library/… ). Tak więc wyliczenie może być ustawione na liczbę całkowitą niezdefiniowaną w wyliczeniu. Zużyj test = 123456;
TamusJRoyce,
3
@TamusJRoyce dziesiętny? intmiałoby dużo więcej sensu. Kiedy wyliczenie miałoby część ułamkową!?!?!
ErikE
Przynajmniej zapewnia to, że odpowiadające stałe wyliczeniowe mają tę samą wartość całkowitą. W końcu wyliczenia są tylko zbiorem stałych całkowitych. C # nie wymusza, aby przypisane wartości były stałymi stałymi wyliczenia, tzn. Wyliczenia nie są bezpieczne w ścisłym tego słowa znaczeniu.
Olivier Jacot-Descombes
98

Powyższe rozwiązania wykorzystujące klasy ze stałymi int nie mają bezpieczeństwa typu. To znaczy, możesz wymyślić nowe wartości, które nie zostały zdefiniowane w klasie. Ponadto nie można na przykład napisać metody przyjmującej jedną z tych klas jako dane wejściowe.

Musisz pisać

public void DoSomethingMeaningFull(int consumeValue) ...

Istnieje jednak oparte na klasach rozwiązanie dawnych czasów Java, kiedy nie było dostępnych wyliczeń. Zapewnia to zachowanie prawie podobne do wyliczania. Jedynym zastrzeżeniem jest to, że tych stałych nie można użyć w instrukcji 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) { /* ... */ }
    // ...
}
Siedem
źródło
7
myślę, że to właściwa odpowiedź. Nie możesz mieć dziedziczenia dla Enum, ale to pozwoli ci nim zarządzać!
robob
11
Ładnie i czysto. +1. Tylko wskazówka, naprawdę nie potrzebujesz wartości int.
Ignacio Soler Garcia
1
Nigdy nie myślałem na wyliczeniach, jakbyś mógł powiedzieć FileOpenMode.Read> FileOpenMode.Write. Jeśli tak jest, potrzebujesz int lub nawet lepiej IEqualityComparer dla tego wyliczenia. Pozdrowienia.
Ignacio Soler Garcia
3
@binki używając losowych objectspowoduje, że wartości będą różne dla każdej instancji zestawu, więc nie będą mogły być serializowane.
ivan_pozdeev
2
Ale to nie pozwala ci „włączyć” MyEnum, prawda? Chodzi mi głównie o to, że używam
wyliczeń
13

Ignorując fakt, że podstawa jest słowem zastrzeżonym, nie można dziedziczyć enum.

Najlepsze, co możesz zrobić, to coś takiego:

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

Ponieważ wszystkie są tego samego typu podstawowego (tj .: int), można przypisać wartość z wystąpienia jednego typu do drugiego, który jest rzutowany. Nie idealne, ale działa.

Pascal
źródło
2
baza jest zarezerwowana, ale baza nie jest
erikkallen
2
Odwołuje się do użycia przez OP bazy jako nazwy enum, to tylko przykładowe imię, jestem pewien
John Rasch
Taki sam jak odpowiedź Genisio.
nawfal
6

Wiem, że ta odpowiedź jest trochę spóźniona, ale tak właśnie skończyłem:

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        
}

Jestem w stanie robić takie rzeczy jak:

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)
{
}
Tono Nam
źródło
2
Liczby magiczne można zastąpić obiektów jak za stackoverflow.com/questions/757684/... . Ale w tym konkretnym przypadku możesz uzyskać to, co najlepsze z obu światów, korzystając z funkcji istniejącej nomenklatury biologicznej, aby zagwarantować wyjątkowość.
ivan_pozdeev
4

Oto co zrobiłem. To, co zrobiłem inaczej, to użycie tej samej nazwy i newsłowa kluczowego w „konsumpcji” enum. Ponieważ nazwa tego samego enumjest taka sama, możesz po prostu bezmyślnie go używać i będzie dobrze. Plus dostajesz inteligencję. Musisz tylko ręcznie zadbać o to, aby ustawienia były kopiowane z bazy i aby były zsynchronizowane. Możesz w tym pomóc wraz z komentarzami do kodu. To kolejny powód, dla którego w bazie danych enumzawsze przechowuję ciąg, a nie wartość. Ponieważ jeśli używasz automatycznie przypisywanych rosnących wartości liczb całkowitych, mogą one ulec zmianie z czasem.

// 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
    }
}
toddmo
źródło
2
Rozważ ustawienie innego zakresu dla nowych wartości w klasie pochodnej (np. SmallMedium = 100,), Aby zachować zgodność ze starszymi wersjami oprogramowania po dodaniu nowych wartości w klasie podstawowej. Na przykład dodanie Hugerozmiaru do wyliczenia podstawowego przypisałoby 4jego wartość, ale 4jest już zajęte SmallMediumw klasie pochodnej.
Roberto,
1
@Roberto, Aby rozwiązać ten problem, nigdy nie utrzymuję wartości wyliczeniowych, a jedynie nazwy. A utrzymanie ich synchronizacji jest tutaj wymogiem. Tak więc dodanie Hugeklasy podstawowej wymagałoby wcześniej Hugew podklasieSmallMedium
toddmo
2
BaseBallmoże nie być tutaj najmądrzejszą nazwą. To wprowadza zamieszanie. Ponieważ baseball jest w rzeczywistości rzeczą. Jeśli tęsknisz za tym, że to tylko podstawowa klasa piłki, wygląda to dziwnie, że siatkówka dziedziczy po fizycznie mniejszym baseballu :). Moja sugestia to po prostu użyćBall
Nick N.
3

Alternatywne rozwiązanie

W mojej firmie unikamy „przeskakiwania projektów”, aby dostać się do nietypowych projektów niższego poziomu. Na przykład nasza warstwa prezentacji / interfejsu API może odwoływać się tylko do naszej warstwy domeny, a warstwa domeny może odwoływać się tylko do warstwy danych.

Jest to jednak problem, gdy istnieją wyliczenia, do których należy odwoływać się zarówno prezentacja, jak i warstwy domen.

Oto rozwiązanie, które wdrożyliśmy (do tej pory). Jest to całkiem dobre rozwiązanie i działa dobrze dla nas. Inne odpowiedzi dotyczyły tego wszystkiego.

Podstawową przesłanką jest to, że nie można dziedziczyć enum - ale klasy mogą. Więc...

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

Następnie, aby „odziedziczyć” wyliczenia w innym projekcie wyższego poziomu ...

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

Ma to trzy prawdziwe zalety ...

  1. Definicje wyliczania są automatycznie takie same w obu projektach - z definicji.
  2. Wszelkie zmiany definicji wyliczenia są automatycznie powtarzane w drugiej sekundzie, bez konieczności wprowadzania jakichkolwiek modyfikacji w drugiej klasie.
  3. Wyliczenia oparte są na tym samym kodzie - więc wartości można łatwo porównać (z pewnymi zastrzeżeniami).

Aby odwołać się do wyliczeń w pierwszym projekcie , możesz użyć prefiksu klasy: BaseEnums.StatusType.Pending lub dodać „przy użyciu statycznego BaseEnums;” oświadczenie dotyczące twoich zastosowań.

Jednak w drugim projekcie dotyczącym odziedziczonej klasy nie mogłem uzyskać podejścia „za pomocą statycznego ...” , więc wszystkie odniesienia do „dziedziczonych wyliczeń” byłyby poprzedzone klasą, np. SubEnums.StatusType.Pending . Jeśli ktoś wymyśli sposób, aby pozwolić na użycie „statycznego” podejścia w drugim projekcie, daj mi znać.

Jestem pewien, że można to ulepszyć, aby uczynić go jeszcze lepszym - ale to faktycznie działa i zastosowałem to podejście w działających projektach.

Proszę głosować za tym, jeśli okaże się to pomocne.

Thomas Phaneuf
źródło
2

Chciałem też przeciążenia wyliczenia i stworzył mieszankę odpowiedzią „Siedem” na tej stronie i odpowiedzią „Merlyn Morgan-Graham” na duplikatu stanowisko to , plus kilka ulepszeń.
Główne zalety mojego rozwiązania w porównaniu do innych:

  • automatyczny przyrost podstawowej wartości int
  • automatyczne nazywanie

Jest to gotowe rozwiązanie i może być bezpośrednio wprowadzone do projektu. Został zaprojektowany zgodnie z moimi potrzebami, więc jeśli nie lubisz niektórych jego części, po prostu zamień je na własny kod.

Po pierwsze, istnieje klasa podstawowa, z CEnumktórej wszystkie niestandardowe wyliczenia powinny dziedziczyć. Ma podstawową funkcjonalność, podobną do Enumtypu .net :

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

Po drugie, oto 2 pochodne klasy Enum. Wszystkie klasy pochodne potrzebują podstawowych metod, aby działać zgodnie z oczekiwaniami. Jest to zawsze ten sam kod bojlera; Nie znalazłem jeszcze sposobu na przeniesienie go do klasy podstawowej. Kod pierwszego poziomu dziedziczenia różni się nieznacznie od wszystkich kolejnych poziomów.

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

Klasy zostały pomyślnie przetestowane przy użyciu następującego kodu:

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
}
Tobias Knauss
źródło
1

Wyliczenia nie są rzeczywistymi klasami, nawet jeśli tak wyglądają. Wewnętrznie są one traktowane jak ich typ bazowy (domyślnie Int32). Dlatego możesz to zrobić tylko poprzez „kopiowanie” pojedynczych wartości z jednego wyliczenia do drugiego i rzutowanie ich na liczbę całkowitą w celu porównania ich pod kątem równości.

Lucero
źródło
1

Wyliczeń nie można wyprowadzać z innych wyliczeń, ale tylko z int, uint, short, ushort, long, Ulong, byte i sbyte.

Jak powiedział Pascal, możesz użyć wartości lub stałych innych wyliczeń do zainicjowania wartości wyliczenia, ale o to chodzi.

Jeroen Landheer
źródło
8
Jest to trochę mylące ze względu na składnię c #, ale wyliczenia nie mogą w rzeczywistości dziedziczyć po int, uint itp. ... Pod maską wciąż dziedziczą po System.Enum. Po prostu członek reprezentujący wyliczenie jest wpisywany na int, uint itp.
JaredPar
@JaredPar. Kiedy wyliczenie jest wyjęte z uinta, oznacza to, że wszystkie wartości są uintem itp. Domyślnie wyliczenie dziedziczy int. (Spójrz na specyfikację C #, enum SomeEnum: uint {...} faktycznie działa.)
Jeroen Landheer
4
Właściwie nie. Dziedziczy System.enum. Jak pisano wcześniej i częściej tutaj, to, co uważasz za dziedziczenie, jest po prostu niedorzecznością językową w csharp.
TomTom
1

inne możliwe rozwiązanie:

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

ShloEmi
źródło
1

Nie jest to możliwe (jak już wspomniano @JaredPar). Próba obejścia tego logiką jest złą praktyką. Jeśli base classmasz taki enum, powinieneś wypisać wszystkie możliwe enum-valuesopcje, a implementacja klasy powinna działać z wartościami, które zna.

Np. Załóżmy, że masz klasę podstawową BaseCatalogi ma ona enum ProductFormats( Digital, Physical). Wtedy możesz mieć MusicCatalogalbo, BookCatalogktóry może zawierać zarówno Digitali Physicalprodukty, ale jeśli klasa jest ClothingCatalog, powinien zawierać tylko Physicalprodukty.

Jaider
źródło
1

Zdaję sobie sprawę, że jestem trochę spóźniony na to przyjęcie, ale oto moje dwa centy.

Wszyscy wiemy, że środowisko nie obsługuje Enum. W tym wątku zasugerowano kilka bardzo interesujących obejść, ale żadne z nich nie przypominało tego, czego szukałem, więc sam to zrobiłem.

Przedstawiamy: ObjectEnum

Możesz sprawdzić kod i dokumentację tutaj: https://github.com/dimi3tron/ObjectEnum .

A pakiet tutaj: https://www.nuget.org/packages/ObjectEnum

Lub po prostu zainstaluj: Install-Package ObjectEnum

W skrócie, ObjectEnum<TEnum> działa jak opakowanie dla każdego wyliczenia. Przesłaniając GetDefinedValues ​​() w podklasach, można określić, które wartości wyliczeniowe są poprawne dla tej konkretnej klasy.

Dodano szereg przeciążeń operatora, aby utworzyć ObjectEnum<TEnum> instancja zachowywała się tak, jakby była instancją bazowego wyliczenia, pamiętając o ograniczeniach dotyczących zdefiniowanych wartości. Oznacza to, że możesz łatwo porównać instancję do wartości int lub enum, a tym samym użyć jej w przypadku przełącznika lub dowolnego innego warunku.

Chciałbym odwołać się do wzmianki o repozytorium github powyżej w celu uzyskania przykładów i dalszych informacji.

Mam nadzieję, że uznasz to za przydatne. Skomentuj lub otwórz problem w witrynie github, aby uzyskać dalsze przemyślenia lub komentarze.

Oto kilka krótkich przykładów tego, co możesz zrobić 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;
}
Dimitri Troncquo
źródło
-8

Możesz wykonywać dziedziczenie w enum, jednak jest ono ograniczone tylko do następujących typów. int, uint, byte, sbyte, short, ushort, long, ulong

Na przykład

public enum Car:int{
Toyota,
Benz,
}
Sriwantha Attanayake
źródło
2
Myślę, że OP poprosił o odziedziczenie jednego wyliczenia od drugiego, nie tylko od podstawowego typu numerycznego (co wszystkie wyliczenia robią w języku C #, pośrednio lub jawnie)
reirab