Enum "Inheritance"

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

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

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

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

Мысли?

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

Ответ 1

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

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

  • Все перечисления должны состоять из System.Enum
  • Из-за вышеизложенного все перечисления являются типами значений и, следовательно, запечатаны

Ответ 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;

Ответ 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);
    }
}

Чтобы пересечь базу и потребление...

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

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

Ответ 4

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

Ответ 5

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

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

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        
}

Затем я могу делать такие вещи, как:

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

Ответ 7

Это то, что я сделал. То, что я сделал по-другому, использует одно и то же имя и ключевое слово new для "потребляющего" enum. Так как имя 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
    }
}

Ответ 8

Перечисления не являются фактическими классами, даже если они выглядят так. Внутри они обрабатываются так же, как и их базовый тип (по умолчанию Int32). Поэтому вы можете сделать это только путем "копирования" одиночных значений из одного перечня в другой и отведения их к их целочисленному числу, чтобы сравнить их для равенства.

Ответ 9

Перечисления не могут быть перегружены из других перечислений, но только из int, uint, short, ushort, long, ulong, byte и sbyte.

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

Ответ 10

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

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

НТН

Ответ 11

Это невозможно (как уже упоминалось @JaredPar). Попытка заставить логику обойти это - плохая практика. Если у вас есть base class, у которого есть enum, вам следует указать список всех возможных enum-values, а реализация класса должна работать со значениями, которые он знает.

например. Предположим, что у вас есть базовый класс BaseCatalog, и он имеет enum ProductFormats (Digital, Physical). Тогда вы можете иметь MusicCatalog или BookCatalog, который может содержать как продукты Digital, так и Physical. Но если класс ClothingCatalog, он должен содержать только Physical.

Ответ 12

Я также хотел перегрузить Enums и создал сочетание ответа "Семь" на этой странице и ответ "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 производных класса 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
}

Ответ 13

Вы можете выполнять наследование в перечислении, однако оно ограничивается только следующими типами. int, uint, byte, sbyte, short, ushort, long, ulong

например.

public enum Car:int{
Toyota,
Benz,
}