Enum “Inheritance”

后端 未结 15 1496
野性不改
野性不改 2020-11-22 07:21

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
{
         


        
相关标签:
15条回答
  • 2020-11-22 08:09

    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
    0 讨论(0)
  • 2020-11-22 08:10

    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,
    }
    
    0 讨论(0)
  • 2020-11-22 08:11

    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) { /* ... */ }
        // ...
    }
    
    0 讨论(0)
  • 2020-11-22 08:12

    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;
    
    0 讨论(0)
  • 2020-11-22 08:14

    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
    }
    
    0 讨论(0)
  • 2020-11-22 08:14

    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;
    }
    
    0 讨论(0)
提交回复
热议问题