Перечисление " Наследование"



У меня есть перечисление в пространстве имен низкого уровня. Я хотел бы предоставить класс или перечисление в пространстве имен среднего уровня, которое "наследует" перечисление низкого уровня.



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

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


Я надеюсь, что это возможно, или, возможно, какой-то класс, который может занять место enum consume, который обеспечит уровень абстракции для перечисления, но все же позволит экземпляру этого класса получить доступ к перечислению.



мысли?



изменить:
Одна из причин, по которой я не просто переключился на это consts в классах заключается в том, что перечисление низкого уровня необходимо для службы, которую я должен использовать. Мне были даны WSDLs и XSDs, которые определяют структуру как перечисление. Сервис не может быть изменен.

592   13  

13 ответов:

Это невозможно. Перечисления не могут наследовать от других перечислений. На самом деле все перечисления должны наследовать от System.Enum. C# позволяет синтаксису изменять базовое представление значений перечисления, которое выглядит как наследование, но на самом деле они все еще наследуются от системы.перечисление.

см. раздел 8.5.2 CLI spec для полной информации. Соответствующая информация из спецификации

  • все перечисления должны быть получены от 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
}

но, это не все, что здорово, потому что база.А != Поглощать.А

вы всегда можете сделать что-то вроде этого, хотя:

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

для того, чтобы пересечь между базой и потреблять...

вы также можете использовать значения перечислений как ints и сравнивать их как ints вместо enum, но это отстой тоже.

метод расширения должен возвращать тип cast IT type T.

решения выше с использованием классов с константами 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) { /* ... */ }
    // ...
}

игнорируя тот факт, что base является зарезервированным словом, вы не можете наследовать перечисление.

лучшее, что вы могли бы сделать, это что-то вроде этого:

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

вот что я сделал. То, что я сделал по-другому, это использовать то же имя и new ключевое слово на "потреблении" enum. Так как имя enum то же, вы можете просто бездумно использовать его, и это будет правильно. Плюс вы получаете intellisense. Вам просто нужно вручную позаботиться о том, чтобы значения копировались из базы и сохраняли их синхронизацию. Вы можете помочь этому вместе с комментариями кода. Это еще одна причина, почему в базе данных при хранении enum значения I всегда храните строку, а не значение. Потому что если вы используете автоматически назначенные увеличивающиеся целочисленные значения, они могут меняться с течением времени.

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

Как сказал Паскаль, вы можете использовать другие значения или константы перечисления для инициализации значения перечисления, но это все.

другое возможное решение:

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 ProductFormats (Digital,Physical). Тогда вы можете иметь MusicCatalog или BookCatalog это может содержать оба Digital и Physical продукты, но если класс ClothingCatalog, он должен содержать только Physical продукты.

Я также хотел перегрузить перечисления и создал смесь ответ "семь" на этой странице и ответ "Мерлина Моргана-Грэма" на дублирующий пост этого, плюс несколько улучшений.
Основные преимущества моего решения перед другими:

  • автоматическое увеличение базового значения 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,
}

Comments

    Ничего не найдено.