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

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

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


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

思路?

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

评论

一种选择是codeproject.com/Articles/20805/Enhancing-C-Enums

您可以使用Int32并根据需要将其类型转换为枚举。

#1 楼

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

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


所有枚举都必须源自System.Enum

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

/>

评论


并且所有值类型都源自System.ValueType

–拉兹(Raz Megrelidze)
2014年3月20日在6:55



不得不提到@Seven的答案是一个合理的解决方法:stackoverflow.com/a/4042826/538387

– Tohid
15年11月23日在15:20

但是@Steven的答案不能在切换情况下使用。

– zionpi
19年4月11日在7:35

@zionpi是的,但请注意(我相信),如果(如果)否则,则将标准开关编译为相同的IL代码,否则,else块会:我认为无论如何它都有更好的语法。您缺乏使reshaper / VS自动完成所有案例陈述的能力,但是我认为这还不是世界末日。这是个人喜好,但我不喜欢switch语句。

– MemeDeveloper
19年7月4日在17:57

#2 楼

您可以使用类实现所需的内容:

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;


评论


那么,您如何枚举此类中的字段?对我来说,枚举的重要行为是:Enum.GetValues(typeof(MyEnum)

–迈克·德·克莱克(Mike de Klerk)
2015年6月3日在7:46



您可以使用反射:void Test(){foreach(typeof(Consume).GetProperties()中的System.Reflection.PropertyInfo pi){Console.WriteLine(pi.Name); }}

–mnieto
2015年6月5日在10:08



您需要确保通过反射收集属性会忽略继承的Object属性。

–罗伯特
15年7月17日在14:11

对于这项任务,反思是非常丑陋的。

– dylanh724
18年7月31日在7:00

无需使用Reflection,codeproject.com / Articles / 20805 / Enhancing-C-Enums上的实现是一种很好的实现方式,因为正在创建对象,将它们添加到列表中,并且可以将该列表用于返回对象类型的列表。将其与继承混合使用时,必须确保对继承类使用的列表正确。

– PBo
18-09-3在9:51



#3 楼

最简洁的答案是不。如果愿意,您可以玩一些游戏:

您总是可以执行以下操作:

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之间进行交叉...

您还可以将枚举的值转换为int,并将它们比较为ints而不是枚举,但是这种情况也很糟糕。

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

评论


我挖这个,伙计。使用此概念将一些枚举从我的ORM冒充到我的公共接口(向那些没有ORM引用的人)。

– ObjectType
13年2月13日在15:15

可以强制枚举以进行比较:Base.A ==(Base)Consume.A

–克雷西米尔
2014年8月29日19:55



使用(十进制)Base.A ==(十进制)Consume.A。原因:这是位标志/掩码组合的工作方式(例如Enum.IsDefinedmsdn.microsoft.com/en-us/library/…中的示例)。因此,可以将枚举设置为枚举中未定义的整数。消耗测试= 123456;

– TamusJRoyce
2015年4月13日4:55



@TamusJRoyce十进制? int会更有意义。枚举何时会占小部分!?!?!

– ErikE
15年7月20日在21:18

至少,这确保了相应的枚举常量具有相同的整数值。毕竟,枚举只是一组整数常量。 C#不会强制分配的值是有效的枚举常量,即严格意义上来说枚举不是类型安全的。

–奥利维尔·雅科特·德ombes
18年2月2日在16:23

#4 楼

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

您需要编写
/>
public void DoSomethingMeaningFull(int consumeValue) ...


但是,在没有枚举可用的情况下,有一种基于类的Java旧解决方案。这提供了几乎类似于枚举的行为。唯一需要注意的是,这些常量不能在开关语句中使用。

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的继承,但这可以让您管理它!

–robob
2011年2月1日7:10

干净整洁。 +1。只是一个提示,您实际上根本不需要int值。

– Ignacio Soler Garcia
2012年6月14日7:35



我从没考虑过枚举,就好像您可以说FileOpenMode.Read> FileOpenMode.Write。如果是这种情况,那么您需要一个int或更好的IEqualityComparer来实现该枚举。问候。

– Ignacio Soler Garcia
2014年4月1日在20:49

@binki使用随机对象将使程序集的每个实例化的值都不同,因此无法序列化它们。

– ivan_pozdeev
2014-12-16 9:20



但这不允许您“切换” MyEnum,对吗?我的意思是这主要是为什么我使用枚举的原因...

– MemphiZ
17-2-7在11:38



#5 楼

忽略base是保留字的事实,您不能继承枚举。

最好的事情是这样的:

/>由于它们都是相同的基本类型(即int),因此您可以将值从一种类型的实例分配给另一种类型的强制类型转换。不理想,但可以。

评论


基地保留,但不是

–erikkallen
09年4月16日在20:02

他指的是OP使用base作为枚举名称,我确定这只是一个示例名称

–约翰·拉施(John Rasch)
09年4月16日在21:39

与Genisio的答案相同。

– nawfal
2014年7月3日在6:50

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


然后我可以做类似的事情:

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


评论


可以根据stackoverflow.com/questions/757684/…将幻数替换为对象。但是对于这种特殊情况,您可以通过利用现有生物学术语的功能来保证两全其美。

– ivan_pozdeev
2014-12-16 9:17



是否有从DogAnimal继承的DogType和AnimalType的原因?从我所看到的可以将它们变成静态类

– Ivaylo Slavov
11月4日8:25

#7 楼

这就是我所做的。我做的不同的是在“消费” new上使用相同的名称和enum关键字。由于enum的名称相同,因此您可以轻而易举地使用它,它将是正确的。另外,您还将获得智能感知。您只需在设置时手动小心,将值从基础复制过来并保持同步即可。您可以在代码注释中提供帮助。这是为什么在数据库中存储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
    }
}


评论


考虑为派生类中的新值设置不同的范围(例如SmallMedium = 100,),以便在基类中添加新值时可以保持与旧版本软件的兼容性。例如,在基本枚举中添加一个“巨大”大小将为其值分配4,但是派生类中的SmallMedium已采用4。

–罗伯托
17年4月2日在10:49

@Roberto,要解决这个问题,我从不保留枚举值,而仅保留名称。保持它们同步是这里的要求。因此,在基类中添加Huge将需要在SmallMedium之前的子类中添加Huge

– toddmo
17年4月2日在21:36

BaseBall可能不是这里最聪明的名称。令人困惑。因为棒球实际上是一回事。如果您错过了这只是球的基础课,那么排球是从较小的棒球继承而来的,这看起来很奇怪:)。我的建议是只使用Ball

–尼克N.
18-3-12在10:25



#8 楼

替代解决方案
在我公司,我们避免“跳过项目”进入非常见的较低级别的项目。例如,我们的表示层/ API层只能引用我们的域层,而域层只能引用数据层。
但是,当存在表示层和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()
   {}
}

这具有三个真正的优点...

两个项目中的枚举定义自动相同-通过
定义。
对枚举定义的任何更改都会在第二个步骤中自动回显
,而无需对
第二类。
枚举基于相同的代码-因此可以轻松地比较值(有一些警告)。

要引用第一个项目中的枚举,您可以使用该类的前缀:BaseEnums.StatusType.Pending或添加“使用静态BaseEnums;”。
然而,在第二个项目中,当处理继承的类时,我无法使用“使用静态...”方法,因此所有对“继承的枚举”的引用都将加上前缀班级,例如SubEnums.StatusType.Pending。如果有人想出一种方法允许在第二个项目中使用“使用静态”方法,请告诉我。
我相信可以对其进行调整以使其变得更好-但这确实有效并且我在工作项目中使用过这种方法。
如果您觉得有帮助,请对其进行投票。

#9 楼

我还想让Enums重载,并在此页面的重复帖子中混合了此页面上的“七个”答案和“梅林·摩根·格雷厄姆”的答案,并进行了一些改进。
主要优点我对其他解决方案的解决方案:


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


#10 楼

我知道我参加这个聚会有点晚了,但这是我的两分钱。
我们很清楚该框架不支持Enum继承。在该线程中提出了一些非常有趣的变通方法,但是它们都不像我想要的那样,因此我自己进行了研究。此处的文档:https://github.com/dimi3tron/ObjectEnum。
此处的包:https://www.nuget.org/packages/ObjectEnum
或直接安装:Install-Package ObjectEnum
ObjectEnum<TEnum>充当任何枚举的包装器。通过覆盖子类中的GetDefinedValues(),可以指定哪些枚举值对此特定类有效。
已添加许多运算符重载,以使ObjectEnum<TEnum>实例的行为就像是基本枚举的实例一样,请牢记定义的值限制。这意味着您可以轻松地将实例与int或enum值进行比较,从而将其用于切换案例或任何其他条件。
希望您觉得这有用。随时发表评论或在github上发表问题,以提出进一步的想法或意见。 > ObjectEnum<TEnum>

#11 楼

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

#12 楼

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

就像Pascal所说的那样,您可以使用其他枚举的值或常量来初始化枚举值,仅此而已。

评论


由于c#语法,这有点用词不当,但是enum实际上不能继承自int,uint等。在幕后,他们仍然继承自System.Enum。只是代表枚举的成员键入了int,uint等。

– JaredPar
09年4月16日在20:12

@JaredPar。当枚举从uint派生时,意味着它的值都是uint,依此类推。默认情况下,枚举继承int。 (看看C#规范,枚举SomeEnum:uint {...}确实有效。)

– Jeroen Landheer
09年4月17日在2:09

其实不行它继承了System.enum。正如之前和这里经常发布的那样,您认为继承只是csharp中的一种语言歧义。

– TomTom
2010-10-28 12:22

#13 楼

另一个可能的解决方案:

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

#14 楼

这是不可能的(就像已经提到的@JaredPar)。试图使逻辑变通解决此问题是一种不好的做法。如果您有一个base class和一个enum,则应在此处列出所有可能的enum-values,并且class的实现应使用其已知的值工作。假设您有一个基类BaseCatalog,并且它有一个enum ProductFormatsDigitalPhysical)。然后,您可以拥有一个MusicCatalogBookCatalog,它们可以同时包含DigitalPhysical产品,但是如果类是ClothingCatalog,则应该只包含Physical产品。

#15 楼

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

例如

public enum Car:int{
Toyota,
Benz,
}


评论


我认为OP要求从另一个继承一个枚举,而不仅仅是从基本数字类型继承(所有枚举在C#中都隐式或显式地继承)。

– reirab
2013年9月6日19:46在