Enum "Inheritance"
Asked Answered
J

18

478

I have an enum in a low level namespace. I'd like to provide a class or enum in a mid level namespace that "inherits" the low level enum.

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

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

I'm hoping that this is possible, or perhaps some kind of class that can take the place of the enum consume which will provide a layer of abstraction for the enum, but still let an instance of that class access the enum.

Thoughts?

EDIT: One of the reasons I haven't just switched this to consts in classes is that the low level enum is needed by a service that I must consume. I have been given the WSDLs and the XSDs, which define the structure as an enum. The service cannot be changed.

Jinja answered 16/4, 2009 at 19:27 Comment(3)
One option is codeproject.com/Articles/20805/Enhancing-C-EnumsPsychotomimetic
You can use an Int32 and just typecast it to your enum as needed.Zakaria
You can use code scripting. Read original code and create new code. I have workflow and workflowfilter. They are the same except for an ALL value. I simply use scripting to create the new workflow filter with the all value added. See T4. Not in the language but there is always a way.Kacikacie
A
537

This is not possible. Enums cannot inherit from other enums. In fact all enums must actually inherit from System.Enum. C# allows syntax to change the underlying representation of the enum values which looks like inheritance, but in actuality they still inherit from System.enum.

See section 8.5.2 of the CLI spec for the full details. Relevant information from the spec

  • All enums must derive from System.Enum
  • Because of the above, all enums are value types and hence sealed
Adalia answered 16/4, 2009 at 19:45 Comment(4)
And all value types derive from System.ValueTypeSoloman
Have to mention that @Seven 's answer is a legitimate workaround: https://mcmap.net/q/27046/-enum-quot-inheritance-quotVeda
but @Steven's answer cannot used under switch situation.Statute
@Statute yes but note that (I believe) the standard switch gets compiled into same IL code as a full if, else if, else block would: which I think has a better better syntax anyway. You do loose ability for resharper / VS to autocomplete all the case statements, but I think that's not the end of the world. It's a personal preference but I'm not a fan of the switch statement.Swore
B
178

You can achieve what you want with classes:

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

Now you can use these classes similar as when they were enums:

int i = Consume.B;

Update (after your update of the question):

If you assign the same int values to the constants as defined in the existing enum, then you can cast between the enum and the constants, e.g:

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;
Binkley answered 16/4, 2009 at 20:0 Comment(9)
How do you enumerate the fields in this class then? For me that is important behavior of an enum: Enum.GetValues(typeof(MyEnum)Hola
You can use reflection: void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } } Margarettamargarette
You'd need to make sure the gathering of properties with reflection ignores the inherited Object properties.Legaspi
Reflection is super ugly for that task.Gluttonize
Nice trick, especially when dealing with enum that are not accessible from frontend to models in data access layer.Nenitanenney
No need to use Reflection, the implementation at codeproject.com/Articles/20805/Enhancing-C-Enums is a good way to do it, as the objects are being created, they are added to a list and that list can be used to return a list of object types. When you mix it with inheritence, you will have to make sure you are using the right list for the inheriting classes.De
This is the kind of thing I feel like I should've thought about but didn't. I can't see any relevant downside against using same enum values... You can define your enums taking values from a base enum i.e.Lighterman
@De Using classes instead of enums looks like a good solution, but it would make sense to use static classes for this, and they can't be inherited either. Creating objects just to use these classes like enums doesn't make sense either. What could be a better solution here?Manche
@JustinasRubinovas, I have used SmartEnums by Ardalis and that seems to work well for about 3 projects so far github.com/ardalis/SmartEnumDe
Y
141

The short answer is no. You can play a bit, if you want:

You can always do something like this:

private enum Base
{
    A,
    B,
    C
}

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

But, it doesn't work all that great because Base.A != Consume.A

You can always do something like this, though:

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

In order to cross between Base and Consume...

You could also cast the values of the enums as ints, and compare them as ints instead of enum, but that kind of sucks too.

The extension method return should type cast it type T.

Yodel answered 16/4, 2009 at 19:36 Comment(9)
I dig this, man. Used this concept to bubble some enums from my ORM to my public interface (to those without the ORM reference).Wrightson
One can cast enums for comparison to work: Base.A == (Base)Consume.ACatlike
Use (decimal)Base.A == (decimal)Consume.A. Reason: This is how bit flag/mask combining works (e.g. example in Enum.IsDefined msdn.microsoft.com/en-us/library/…). So an enum can be set to an integer not defined in the enum. Consume test = 123456;Salvucci
@Salvucci decimal? int would make a lot more sense. When would an enum have a fractional part!?!?!Bolide
At least, this ensures that corresponding enum constants have the same integer value. After all, enums are just a set of integer constants. C# does not enforce that the assigned values are vaild enum constants, i.e. enums are not type safe in the strict sense.Wootan
@OlivierJacot-Descombes Why are you addressing your comment to me? I am against decimal enums.Bolide
Okay. This was a mistake. I deleted my comment. Sorry.Wootan
An enum can be of any primary type, and ismuch more than a set of integer constants. enum MyEnum : byte { None = 0, ValueA = 0x_4F, ValueR = 0x_7F } for example makes it already more valuable than a simple enum.Bice
Just tried out your phrase "But, it doesn't work all that great because Base.A != Consume.A" in .NET Fiddle. It may be best to say, "But it doesn't work all that great, because you have to cast Base.A and Consume.A both to int or something, if you want to check for equality." Otherwise this is a really good answer.Mantling
L
111

The solutions above using classes with int constants lack type-safety. I.e. you could invent new values actually not defined in the class. Furthermore it is not possible for example to write a method taking one of these classes as input.

You would need to write

public void DoSomethingMeaningFull(int consumeValue) ...

However, there is a class based solution of the old days of Java, when there were no enums available. This provides an almost enum-like behaviour. The only caveat is that these constants cannot be used within a switch-statement.

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) { /* ... */ }
    // ...
}
Lampe answered 28/10, 2010 at 12:19 Comment(7)
i think this is the right answer. You cannot have inheritance for Enum but this can allow you to manage it!Baseler
Nice and clean. +1. Just a hint, you really don't need the int value at all.Rosena
@SoMoS, are you suggesting that object be used like recommended for lock? I cannot think of a nice way to make that idea compatible with the “numeration” part of enum where MyEnum.First < MyEnum.Second. But if ordering isn’t important for your enum, then that’d be a nifty way to avoid the magic int constants.Nuclease
I never thought on enumerations as if you can say FileOpenMode.Read > FileOpenMode.Write. If that's the case then you need an int or even better a IEqualityComparer for that enum. Regards.Rosena
@Nuclease using random object's would make the values different for each instantiation of the assembly so they cannot be serialized.Lesleelesley
But this does not allow you to "switch" on MyEnum, does it? I mean that's mainly why I use enums...Smoothspoken
@MemphiZ, no you can't. A switch needs an constant expression, so unfortunately you even cannot switch on the property InternalValueLampe
A
15

Ignoring the fact that base is a reserved word you cannot do inheritance of enum.

The best thing you could do is something like that:

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

Since they're all the same base type (ie: int) you could assign the value from an instance of one type to the other which a cast. Not ideal but it work.

Apologia answered 16/4, 2009 at 19:44 Comment(3)
base is reserved but Base isn'tConfined
He's referring to the OP's use of base as the enum name, it's just an example name I'm sureOminous
Same as Genisio's answer.Ninetieth
E
11

This is what I did. What I've done differently is use the same name and the new keyword on the "consuming" enum. Since the name of the enum is the same, you can just mindlessly use it and it will be right. Plus you get intellisense. You just have to manually take care when setting it up that the values are copied over from the base and keep them sync'ed. You can help that along with code comments. This is another reason why in the database when storing enum values I always store the string, not the value. Because if you are using automatically assigned increasing integer values those can change over time.

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

public class VolleyBall : Ball
{
    // keep synced with base class!
    public new enum Sizes
    {
        Small  = Ball.Sizes.Small,
        Medium = Ball.Sizes.Medium,
        Large  = Ball.Sizes.Large,
        SmallMedium,
        MediumLarge,
        Ginormous
    }
}
Equites answered 24/2, 2017 at 19:21 Comment(2)
Consider setting a different range for the new values in the derived class (e.g. SmallMedium = 100,), so you can keep compatibility with older versions of your software when you add new values in the base class. For example, adding a Huge size in your base enum would assign 4 to its value, but 4 is already taken by SmallMedium in the derived class.Arvad
@Roberto, To address this, I never persist enum values, but only the names. And keeping them synced is a requirement here. So adding Huge in the base class would need a Huge in the subclass before SmallMediumEquites
T
7

I know this answer is kind of late but this is what I ended up doing:

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        
}

Then I am able to do things like:

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)
{
}
Trass answered 2/5, 2014 at 14:10 Comment(2)
Magic numbers can be replaced with objects as per #758184 . But for this particular case, you can get the best of both worlds by using the features of the existing biological nomenclature to guarantee uniqueness.Lesleelesley
Is there a reason DogType and AnimalType to inherit from BaseAnimal ?. These can be made into static classes from what I am seeingColier
A
6

Alternative solution

In my company, we avoid "jumping over projects" to get to non-common lower level projects. For instance, our presentation/API layer can only reference our domain layer, and the domain layer can only reference the data layer.

However, this is a problem when there are enums that need to be referenced by both the presentation and the domain layers.

Here is the solution that we have implemented (so far). It is a pretty good solution and works well for us. The other answers were hitting all around this.

The basic premise is that enums cannot be inherited - but classes can. So...

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

Then, to "inherit" the enums in another higher level project...

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

This has three real advantages...

  1. The enum definitions are automatically the same in both projects - by definition.
  2. Any changes to the enum definitions are automatically echoed in the second without having to make any modifications to the second class.
  3. The enums are based on the same code - so the values can easily be compared (with some caveats).

To reference the enums in the first project, you can use the prefix of the class: BaseEnums.StatusType.Pending or add a "using static BaseEnums;" statement to your usings.

In the second project when dealing with the inherited class however, I could not get the "using static ..." approach to work, so all references to the "inherited enums" would be prefixed with the class, e.g. SubEnums.StatusType.Pending. If anyone comes up with a way to allow the "using static" approach to be used in the second project, let me know.

I am sure that this can be tweaked to make it even better - but this actually works and I have used this approach in working projects.

Ade answered 25/12, 2019 at 2:46 Comment(4)
IMHO what you do is a misinterpretation of layered architecture. contract like structures such as transport objects, interfaces etc. may and should be shared between layers. with restricting that you only tie your own hands and sometimes loose contextual information during your processing pipeline, which information should be recovered later in the pipeline from context and default values. I've seen that mistake done many times. modern frameworks like GRPC also suggest sharing these structures vertically along your architecture.Rovner
This solution answers the question that was asked and works in the absence of a common shared source for the enums. I would not characterize this as a "mistake".Ade
Alright it's not a mistake, it's an anti pattern. You treat shared structure definitions as layers which are not. Then you need to use notation, prefixes and suffixes in order to distinguish them. I always try to name things differently along my app. If I cannot find the proper name for it I analyse and reconsider whether I need that at all. I avoid using notation and convention. That is only additional knowledge to learn and remember. And I share as much code I can. That is the way to eliminates redundancies. I am not my enemy after allRovner
Never said this was the best way. What I did say is that it answered the question that was asked. And sometimes that is all software can do given a specific context. There are situations where definitions cannot be shared from a common source - this answer addresses that.Ade
S
3

I realize I'm a bit late to this party, but here's my two cents.

We're all clear that Enum inheritance is not supported by the framework. Some very interesting workarounds have been suggested in this thread, but none of them felt quite like what I was looking for, so I had a go at it myself.

Introducing: ObjectEnum

You can check the code and documentation here: https://github.com/dimi3tron/ObjectEnum.

And the package here: https://www.nuget.org/packages/ObjectEnum

Or just install it: Install-Package ObjectEnum

In short, ObjectEnum<TEnum> acts as a wrapper for any enum. By overriding the GetDefinedValues() in subclasses, one can specify which enum values are valid for this specific class.

A number of operator overloads have been added to make an ObjectEnum<TEnum> instance behave as if it were an instance of the underlying enum, keeping in mind the defined value restrictions. This means you can easily compare the instance to an int or enum value, and thus use it in a switch case or any other conditional.

I'd like to refer to the github repo mentioned above for examples and further info.

I hope you find this useful. Feel free to comment or open an issue on github for further thoughts or comments.

Here are a few short examples of what you can do with 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;
}
Schnapp answered 25/10, 2019 at 12:51 Comment(0)
P
2

I also wanted to overload Enums and created a mix of the answer of 'Seven' on this page and the answer of 'Merlyn Morgan-Graham' on a duplicate post of this, plus a couple of improvements.
Main advantages of my solution over the others:

  • automatic increment of the underlying int value
  • automatic naming

This is an out-of-the-box solution and may be directly inserted into your project. It is designed to my needs, so if you don't like some parts of it, just replace them with your own code.

First, there is the base class CEnum that all custom enums should inherit from. It has the basic functionality, similar to the .net Enum type:

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

Secondly, here are 2 derived Enum classes. All derived classes need some basic methods in order to work as expected. It's always the same boilerplate code; I haven't found a way yet to outsource it to the base class. The code of the first level of inheritance differs slightly from all subsequent levels.

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

The classes have been successfully tested with follwing code:

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
}
Peeler answered 2/7, 2017 at 20:33 Comment(0)
H
1

Enums are not actual classes, even if they look like it. Internally, they are treated just like their underlying type (by default Int32). Therefore, you can only do this by "copying" single values from one enum to another and casting them to their integer number to compare them for equality.

Hybridism answered 16/4, 2009 at 19:39 Comment(0)
S
1

Enums cannot be derrived from other enums, but only from int, uint, short, ushort, long, ulong, byte and sbyte.

Like Pascal said, you can use other enum's values or constants to initialize an enum value, but that's about it.

Stymie answered 16/4, 2009 at 19:50 Comment(3)
It's a bit of a misnomer because of the c# syntax but enum's cannot actually inherit from int,uint,etc ... Under the hood they still inherit from System.Enum. It's just that the member representing the enum is typed to the int,uint, etc ...Adalia
@JaredPar. When an enum is derrived from an uint, it means it's values are all uint, etc. By default an enum inherits int. (Take a look at the C# specification, enum SomeEnum : uint { ... } actually works.)Stymie
Actually no. It inherits System.enum. As was posted before and more often here, what you think is inheritance is just a langauge dusambiguity in csharp.Landed
D
1

another possible solution:

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

Disseminate answered 16/11, 2016 at 15:30 Comment(0)
O
1

This is not possible (as @JaredPar already mentioned). Trying to put logic to work around this is a bad practice. In case you have a base class that have an enum, you should list of all possible enum-values there, and the implementation of class should work with the values that it knows.

E.g. Supposed you have a base class BaseCatalog, and it has an enum ProductFormats (Digital, Physical). Then you can have a MusicCatalog or BookCatalog that could contains both Digital and Physical products, But if the class is ClothingCatalog, it should only contains Physical products.

Onyx answered 20/3, 2017 at 16:47 Comment(0)
S
0

The way you do this, if warranted, is to implement your own class structure that includes the features you wanted from your concept of an inherited enum, plus you can add more. You simply implement equality comparators and functions to look up values you simply code yourself. You make the constructors private and declare static instances of the class and any subclasses to whatever extent you want. Or find a simple work around for your problem and stick with the native enum implementation.

Code Heavy Implementation of Inherited Enumerations:

/// <summary>
/// Generic Design for implementing inheritable enum
/// </summary>
public class ServiceBase
{

    //members
    protected int _id;
    protected string _name;

    //constructors
    private ServiceBase(int id, string name)
    {
        _id = id;
        _name = name;
    }

    //onlu required if subclassing
    protected ServiceBase(int id, string name, bool isSubClass = true )
    {
        if( id <= _maxServiceId )
            throw new InvalidProgramException("Bad Id in ServiceBase" );
        _id = id;
        _name = name;
        
    }

    //members
    public int Id => _id;
    public string Name => _name;
    public virtual ServiceBase getService(int serviceBaseId)
    {
        return ALLBASESERVICES.SingleOrDefault(s => s.Id == _id);
    }
    
    //implement iComparable if required
    
    //static methods
    public static ServiceBase getServiceOrDefault(int serviceBaseId)
    {
        return SERVICE1.getService(serviceBaseId);
    }

    //Enumerations Here
    public static ServiceBase SERVICE1 = new ServiceBase( 1, "First Service" );
    public static ServiceBase SERVICE2 = new ServiceBase( 2, "Second Service" );

    protected static ServiceBase[] ALLBASESERVICES =
    {
        //Enumerations list
        SERVICE1,
        SERVICE2
    };
    private static int _maxServiceId = ALLBASESERVICES.Max( s => s.Id );

    //only required if subclassing
    protected static ServiceBase[] combineServices(ServiceBase[] array1, ServiceBase[] array2)
    {
        List<ServiceBase> serviceBases = new List<ServiceBase>();
        serviceBases.AddRange( array1 );
        serviceBases.AddRange( array2 );
        return serviceBases.ToArray();
    }

}

/// <summary>
/// Generic Design for implementing inheritable enum
/// </summary>
public class ServiceJobs : ServiceBase
{
    
    //constructor
    private ServiceJobs(int id, string name)
    : base( id, name )
    {
        _id = id;
        _name = name;
    }

    //only required if subclassing
    protected ServiceJobs(int id, string name, bool isSubClass = true )
    : base( id, name )
    {
        if( id <= _maxServiceId )
            throw new InvalidProgramException("Bad Id in ServiceJobs" );
        _id = id;
        _name = name;
        
    }

    //members
    public override ServiceBase getService(int serviceBaseId)
    {
        if (ALLSERVICES == null)
        {
            ALLSERVICES = combineServices(ALLBASESERVICES, ALLJOBSERVICES);
        }
        return ALLSERVICES.SingleOrDefault(s => s.Id == _id);
    }

    //static methods
    public static ServiceBase getServiceOrDefault(int serviceBaseId)
    {
        return SERVICE3.getService(serviceBaseId);
    }

    //sub class services here
    public static ServiceBase SERVICE3 = new ServiceJobs( 3, "Third Service" );
    public static ServiceBase SERVICE4 = new ServiceJobs( 4, "Forth Service" );
    private static int _maxServiceId = ALLJOBSERVICES.Max( s => s.Id );

    private static ServiceBase[] ALLJOBSERVICES =
    {
        //subclass service list
        SERVICE3,
        SERVICE4
    };

    //all services including superclass items
    private static ServiceBase[] ALLSERVICES = null;

}

Note that you can use an enum instead of an int as the id, though the subclass will need a separate enum. The enum class itself can be decorated with all kinds of flags, messages, functions etc. A generic implementation would reduce a great deal of the code.

Snowshed answered 6/7, 2022 at 2:36 Comment(0)
T
0

Depending on your situation you may NOT need derived Enums as they're based off System.Enum.

Take this code, you can pass in any Enum you like and get its selected value:

public CommonError FromErrorCode(Enum code)
{
    Code = (int)Enum.Parse(code.GetType(), code.ToString());
Tullis answered 21/10, 2022 at 8:7 Comment(0)
F
0

Although the answer is no, but enums can be "extended" externally.
The following actually legal on compile and practical within controllable boundaries.
[C# Code]

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

namespace mid
{
    using low;

    public static class ConsumeEnum
    {
        //for constaint value
        public const BaseEnum a = (BaseEnum)101;
        public const BaseEnum b = (BaseEnum)102;
        public const BaseEnum c = (BaseEnum)103;
        //for runtime variable
        public static readonly BaseEnum A = a;
        public static readonly BaseEnum B = b;
        public static readonly BaseEnum C = c;
    }
}

namespace use
{
    using low;

    using mid;

    public class Test
    {
        public void Method(BaseEnum arg)
        {
            switch (arg)
            {
                case BaseEnum.x:
                    break;
                case BaseEnum.y:
                    break;
                case BaseEnum.z:
                    break;
                case ConsumeEnum.a:
                    break;
                case ConsumeEnum.b:
                    break;
                case ConsumeEnum.c:
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(arg), arg, null);
            }
        }
    }
}

Although you really can't use reflection or some other way to use type inheritance here. But you can control your code, and use other ways instead of reflection solutions.

Fuegian answered 12/3 at 2:15 Comment(0)
Q
-9

You can perform inheritance in enum, however it's limited to following types only . int, uint, byte, sbyte, short, ushort, long, ulong

E.g.

public enum Car:int{
Toyota,
Benz,
}
Quincentenary answered 14/12, 2011 at 14:14 Comment(1)
I think the OP was asking to inherit one enum from another, not just from a base numeric type (which all enums do in C#, either implicitly or explicitly.)Caughey

© 2022 - 2024 — McMap. All rights reserved.