首页 文章

枚举“继承”

提问于
浏览
339

我在低级命名空间中有一个枚举 . 我想在一个“继承”低级枚举的中级命名空间中提供一个类或枚举 .

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

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

我希望这是可能的,或者某种类可以代替枚举消耗,它将为枚举提供一层抽象,但仍然让该类的实例访问枚举 .

思考?

编辑:我之前没有将其转换为类中的consts的原因之一是我必须使用的服务需要低级枚举 . 我得到了WSDL和XSD,它们将结构定义为枚举 . 该服务无法更改 .

13 回答

  • 103

    这是不可能的 . 枚举不能从其他枚举继承 . 事实上,所有枚举必须实际上从 System.Enum 继承 . C#允许语法更改看起来像继承的枚举值的基础表示,但实际上它们仍然继承自System.enum .

    有关详细信息,请参见CLI spec的第8.5.2节 . 来自规范的相关信息

    • 所有枚举必须来自 System.Enum

    • 由于上述原因,所有枚举都是值类型,因此是密封的

  • 4

    您可以通过课程实现您想要的目标:

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

    现在,您可以使用与枚举时类似的类:

    int i = Consume.B;
    

    Update (更新问题后):

    如果将相同的int值分配给现有枚举中定义的常量,则可以在枚举和常量之间进行转换,例如:

    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;
    
  • 6

    最简洁的答案是不 . 如果你愿意,你可以玩一点:

    你总是可以这样做:

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

    但是,它并没有那么好用,因为Base.A!= Consume.A

    但是你可以做这样的事情:

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

    为了跨越基地和消费...

    您还可以将枚举的值转换为整数,并将它们作为整数而不是整数进行比较,但这种方式也很糟糕 .

    扩展方法返回应该键入类型T的类型 .

  • 424

    使用具有int常量的类的上述解决方案缺乏类型安全性 . 即您可以创建实际上未在类中定义的新值 . 此外,例如,不可能编写将这些类之一作为输入的方法 .

    你需要写

    public void DoSomethingMeaningFull(int consumeValue) ...
    

    但是,当没有可用的枚举时,有一个基于类的Java解决方案 . 这提供了几乎类似于枚举的行为 . 唯一需要注意的是,这些常量不能在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) { /* ... */ }
        // ...
    }
    
  • 151

    忽略base是保留字的事实,你不能继承enum .

    你能做的最好的事情是:

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

    由于它们都是相同的基类型(即:int),因此您可以将一个类型的实例的值分配给另一个类型的实例 . 不理想,但它的工作 .

  • -6

    我知道这个答案有点晚了,但这就是我最终做的事情:

    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)
    {
    }
    
  • 0

    这就是我所做的 . 我所做的不同的是在"consuming" enum 上使用相同的名称和 new 关键字 . 由于 enum 的名称是相同的,你可以盲目地使用它,它是正确的 . 另外,你得到intellisense . 您只需在设置时手动注意从基础复制值并保持同步 . 您可以提供代码注释 . 这是另一个原因,在数据库中存储 enum 值时,我总是存储字符串,而不是值 . 因为如果使用自动分配的递增整数值,那么这些值可能会随时间而变化 .

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

    枚举不是实际的类,即使它们看起来像它 . 在内部,它们被视为基础类型(默认情况下为Int32) . 因此,您只能通过将单个值从一个枚举“复制”到另一个枚举并将它们转换为整数来比较它们的相等性来实现 .

  • 1

    枚举不能从其他枚举中获取,而只能从int,uint,short,ushort,long,ulong,byte和sbyte中获取 .

    就像Pascal所说,你可以使用其他枚举值或常量来初始化枚举值,但这就是它 .

  • 1

    另一个可能的解

    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

  • 86

    这是不可能的(正如@JaredPar已经提到的那样) . 试图让逻辑解决这个问题是一种不好的做法 . 如果你有 base class 有一个 enum ,你应该列出所有可能的 enum-values ,并且class的实现应该使用它知道的值 .

    例如 . 假设你有一个基类 BaseCatalog ,它有 enum ProductFormatsDigitalPhysical ) . 然后你可以拥有一个 MusicCatalogBookCatalog ,它可以包含 DigitalPhysical 产品,但如果该类是 ClothingCatalog ,它应该只包含 Physical 产品 .

  • 12

    我还想重载Enums并创建了the answer of 'Seven' on this pagethe answer of 'Merlyn Morgan-Graham' on a duplicate post of this的混合,以及一些改进 .
    我的解决方案主要优于其他解决方案:

    • 底层的自动增量int值

    • 自动命名

    这是一个开箱即用的解决方案,可以直接插入您的项目中 . 它是根据我的需求而设计的,所以如果你不喜欢它的某些部分,只需用你自己的代码替换它们 .

    首先,所有自定义枚举都应该继承的基类 CEnum . 它具有基本功能,类似于.net Enum 类型:

    public class CEnum
    {
      protected static readonly int msc_iUpdateNames  = int.MinValue;
      protected static int          ms_iAutoValue     = -1;
      protected static List<int>    ms_listiValue     = new List<int>();
    
      public int Value
      {
        get;
        protected set;
      }
    
      public string Name
      {
        get;
        protected set;
      }
    
      protected CEnum ()
      {
        CommonConstructor (-1);
      }
    
      protected CEnum (int i_iValue)
      {
        CommonConstructor (i_iValue);
      }
    
      public static string[] GetNames (IList<CEnum> i_listoValue)
      {
        if (i_listoValue == null)
          return null;
        string[] asName = new string[i_listoValue.Count];
        for (int ixCnt = 0; ixCnt < asName.Length; ixCnt++)
          asName[ixCnt] = i_listoValue[ixCnt]?.Name;
        return asName;
      }
    
      public static CEnum[] GetValues ()
      {
        return new CEnum[0];
      }
    
      protected virtual void CommonConstructor (int i_iValue)
      {
        if (i_iValue == msc_iUpdateNames)
        {
          UpdateNames (this.GetType ());
          return;
        }
        else if (i_iValue > ms_iAutoValue)
          ms_iAutoValue = i_iValue;
        else
          i_iValue = ++ms_iAutoValue;
    
        if (ms_listiValue.Contains (i_iValue))
          throw new ArgumentException ("duplicate value " + i_iValue.ToString ());
        Value = i_iValue;
        ms_listiValue.Add (i_iValue);
      }
    
      private static void UpdateNames (Type i_oType)
      {
        if (i_oType == null)
          return;
        FieldInfo[] aoFieldInfo = i_oType.GetFields (BindingFlags.Public | BindingFlags.Static);
    
        foreach (FieldInfo oFieldInfo in aoFieldInfo)
        {
          CEnum oEnumResult = oFieldInfo.GetValue (null) as CEnum;
          if (oEnumResult == null)
            continue;
          oEnumResult.Name = oFieldInfo.Name;
        }
      }
    }
    

    其次,这里有2个派生的Enum类 . 所有派生类都需要一些基本方法才能按预期工作 . 它始终是相同的样板代码;我还没有找到将其外包给基类的方法 . 第一级继承的代码与所有后续级别略有不同 .

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

    已经使用以下代码成功测试了这些类:

    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

    您可以在枚举中执行继承,但它仅限于以下类型 . int,uint,byte,sbyte,short,ushort,long,ulong

    例如 .

    public enum Car:int{
    Toyota,
    Benz,
    }
    

相关问题