枚举“继承”

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

namespace low { public enum base { x, y, z } } namespace mid { public enum consume : low.base { } } 

我希望这是可能的,或者也许某种类可以取代枚举消费,这将为枚举提供一个抽象层,但仍然让该类的一个实例访问枚举。

思考?

编辑:其中一个原因,我不是刚刚在类中切换到常量是低级枚举是必需的服务,我必须使用。 我已经获得了WSDLs和XSDs,它们将结构定义为一个枚举。 该服务无法更改。

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

有关完整的详细信息,请参阅CLI规范的第8.5.2节。 规范的相关信息

  • 所有的枚举都必须从System.Enum派生
  • 由于以上所述,所有枚举都是值类型,因此被封闭

你可以用类来实现你想要的东西:

 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; 

更新 (在您更新问题后):

如果将相同的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; 

简短的答案是否定的。 你可以玩一下,如果你想要的话:

你总是可以这样做:

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

为了在Base和Consume之间交叉…

你也可以将枚举的值作为整数,然后将它们作为整数进行比较,而不是枚举,但也是如此。

扩展方法返回应该类型转换为类型T.

上面使用int常量类的解决方案缺乏类型安全性。 也就是说,你可以发现实际上没有在课堂上定义的新值。 此外,例如不可能编写以这些类中的一个作为输入的方法。

你需要写

 public void DoSomethingMeaningFull(int consumeValue) ... 

然而,当Java没有可用的枚举时,有一个基于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) { /* ... */ } // ... } 

忽略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),所以可以将一个类型的实例的值分配给另一个类型的实例。 不理想,但它的工作。

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

 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 } 

然后我可以做这样的事情:

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

这就是我所做的。 我所做的不同之处是在“消费” 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 } } 

枚举不是实际的类,即使它们看起来像。 在内部,它们就像它们的底层类型一样(默认是Int32)。 因此,只能通过将单个值从一个枚举“复制”到另一个枚举并将它们转换为整数来比较它们的相等性。

枚举不能从其他枚举中被赋值,而只能从int,uint,short,ushort,long,ulong,byte和sbyte被赋值。

就像Pascal说的那样,你可以使用其他枚举的值或常量来初始化一个枚举值,但就是这样。

另一个可能的解

 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

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

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

我也想重载Enums,并在这个页面上创建了“Seven” 的答案,以及“Merlyn Morgan-Graham”的复本 ,还有一些改进。
我的解决方案比其他的主要优点:

  • 自动增加底层的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个派生的枚举类。 所有派生类都需要一些基本方法才能按预期工作。 它始终是相同的样板代码; 我还没有找到办法将它外包给基础班。 第一级继承的代码与所有后续级别略有不同。

 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 } 

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

例如

 public enum Car:int{ Toyota, Benz, }