Enum“Inheritance"

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

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


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

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

想法吗?

< p >编辑: 我没有在类中将其转换为const的原因之一是我必须使用的服务需要低级枚举。我已经获得了wsdl和xsd,它们将结构定义为枚举。

.使用实例
301657 次浏览

简短的回答是否定的。如果你想,你可以玩一点:

你可以这样做:

private enum Base
{
A,
B,
C
}


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

但是,它并没有那么好,因为Base。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);
}
}

为了在基础和消费之间交叉…

你也可以将枚举的值转换为整数,并将它们比较为整数而不是enum,但这也很糟糕。

扩展方法return应该将其类型转换为T类型。

枚举并不是真正的类,即使它们看起来像类。在内部,它们被视为它们的基础类型(默认为Int32)。因此,您只能通过将单个值从一个枚举“复制”到另一个枚举,并将它们转换为整数来比较它们是否相等来做到这一点。

忽略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),你可以将一个类型的实例的值赋给另一个类型,这就是强制转换。虽然不理想,但很有效。

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

详细信息请参见CLI规范的8.5.2节。来自规范的相关信息

  • 所有枚举必须派生自System.Enum
  • 因此,所有枚举都是值类型,因此是密封的

枚举不能从其他枚举派生,只能从int、uint、short、ushort、long、ulong、byte和sbyte派生。

就像Pascal说的,你可以使用其他枚举的值或常量来初始化一个枚举值,但仅此而已。

你可以通过类实现你想要的:

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;
以上使用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) { /* ... */ }
// ...
}
你可以在enum中执行继承,但是它仅限于以下类型。 Int, uint, byte, sbyte, short, ushort, long, ulong

如。

public enum Car:int{
Toyota,
Benz,
}

我知道这个回答有点晚了,但这就是我最后做的:

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

另一个可能的解决方案:

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

这就是我所做的。我所做的不同之处在于使用相同的名称,并在“消费”上使用new关键字;enum。由于enum的名称是相同的,你可以不加思考地使用它,它将是正确的。而且你会有智能感知。你只需要在设置时手动注意,这些值是从基数复制过来的,并保持同步。您可以通过代码注释来帮助它。这是另一个原因,为什么在数据库中存储enum值时,我总是存储字符串,而不是值。因为如果你使用的是自动分配的递增整数值,这些值会随着时间而改变。

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

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

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

我还想重载枚举,并创建了这一页上的“7”的答案“梅林·摩根-格雷厄姆”的答案在这篇文章的副本上的混合,再加上一些改进 我的解决方案相对于其他解决方案的主要优势:

  • 基础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
}

我知道我来这个派对有点晚了,但这是我的意见。

我们都很清楚,框架不支持Enum继承。在这篇文章中提出了一些非常有趣的解决办法,但没有一个是我想要的,所以我自己尝试了一下。

介绍:ObjectEnum

你可以在这里查看代码和文档:https://github.com/dimi3tron/ObjectEnum

这里的包:https://www.nuget.org/packages/ObjectEnum

或者直接安装它:Install-Package ObjectEnum

简而言之,ObjectEnum<TEnum>充当任何enum的包装器。通过重写子类中的GetDefinedValues(),可以指定哪些enum值对这个特定的类有效。

添加了许多操作符重载,以使ObjectEnum<TEnum>实例的行为就像底层枚举的实例一样,记住已定义的值限制。这意味着您可以轻松地将实例与int或enum值进行比较,从而在开关情况或任何其他条件中使用它。

我想参考上面提到的github回购的例子和进一步的信息。

我希望这对你有用。请随意评论或在github上打开一个问题以获得进一步的想法或评论。

下面是几个简短的例子,说明你可以用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;
}

可选择的解决方案

在我的公司,我们避免“跳过项目”。以达到不常见的低层次项目。例如,我们的表示层/API层只能引用域层,而域层只能引用数据层。

但是,当表示层和域层都需要引用枚举时,这就出现了问题。

下面是我们已经实现的解决方案(到目前为止)。这是一个很好的解决方案,对我们很有效。其他答案都围绕着这个问题。

基本的前提是枚举不能被继承——但是类可以。所以…

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

那么,“继承”;另一个高级项目中的枚举……

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

这有三个真正的优势……

  1. 两个项目中的枚举定义自动相同- by 李定义。< / >
  2. 对枚举定义的任何更改都是自动的 在不需要做任何修改的情况下 李二等。< / >
  3. 枚举基于相同的代码-因此值可以很容易地进行比较(有一些注意事项)。

要引用第一个项目中的枚举,可以使用类的前缀:BaseEnums.StatusType.Pending或在usings中添加使用静态BaseEnums;语句。

然而,在第二个项目中处理继承类时,我无法让“使用静电……”方法工作,因此所有对“继承的枚举”的引用;将以类作为前缀,例如SubEnums.StatusType.Pending。如果有人提出了一种方法,允许“使用static"方法在第二个项目中使用,请告诉我。

我相信这是可以调整的,使它变得更好——但这实际上是有效的,我已经在工作项目中使用了这种方法。

你这样做的方式,如果有必要,是实现你自己的类结构,其中包括你想从继承枚举的概念中获得的特性,而且你可以添加更多。 您只需实现相等比较器和函数来查找您自己编写的值。 您可以将构造函数设为私有,并声明类和任何子类的静态实例。 或者为您的问题找到一个简单的解决方法,并坚持使用本机enum实现

继承枚举的重代码实现:

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


}
注意,你可以使用枚举而不是int作为id,尽管子类将需要一个单独的枚举。 枚举类本身可以用各种标志、消息、函数等来装饰。 泛型实现将减少大量代码

根据你的情况,你可能需要派生enum,因为它们基于System.Enum

使用这段代码,你可以传入任何你喜欢的Enum,并获得它所选的值:

public CommonError FromErrorCode(Enum code)
{
Code = (int)Enum.Parse(code.GetType(), code.ToString());