Можно ли определить перечисление в C # так, чтобы оно было надмножеством другого перечисления? [Дубликат]

Вы можете использовать словари для этого. Словари - это хранилища ключей и ценностей.

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2

Вы можете использовать имена переменных ключей для достижения эффекта переменных переменных без риска для безопасности.

>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'

В тех случаях, когда вы думаете сделать что-то вроде

var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

список может быть более подходящим, чем dict. Список представляет упорядоченную последовательность объектов с целыми индексами:

l = ['foo', 'bar', 'baz']
print(l[1])           # prints bar, because indices start at 0
l.append('potatoes')  # l is now ['foo', 'bar', 'baz', 'potatoes']

Для упорядоченных последовательностей списки удобнее, чем dict с целыми ключами, потому что списки поддерживают итерацию в порядке индекса, slicing , append и другие операции, которые потребуют неудобного управления ключами с помощью dict.

322
задан CodeMonkey1313 16 April 2009 в 21:04
поделиться

13 ответов

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

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

  • Все перечисления должны быть получены из System.Enum
  • . Из-за вышеперечисленного все перечисления являются типами значений и, следовательно, запечатаны
409
ответ дан Krumia 23 August 2018 в 02:30
поделиться

Короткий ответ - нет. Вы можете играть немного, если хотите:

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

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

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

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

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

100
ответ дан Community 23 August 2018 в 02:30
поделиться
  • 1
    Я копаю это, мужик. Использовал эту концепцию, чтобы пузырить некоторые перечисления из моего ORM в мой открытый интерфейс (для тех, у кого нет ссылки ORM). – ObjectType 13 February 2013 в 17:15
  • 2
    Для сравнения можно использовать перечисления: Base.A == (Base)Consume.A – Kresimir 29 August 2014 в 20:55
  • 3
    Использовать (десятичный) Base.A == (десятичный) Consume.A. Причина. Так работает объединение битов / масок (например, в Enum.IsDefined msdn.microsoft.com/en-us/library/… ). Таким образом, перечисление может быть установлено в целое число, не определенное в перечислении. Потребляемый тест = 123456; – TamusJRoyce 13 April 2015 в 04:55
  • 4
    @TamusJRoyce десятичный? int будет иметь больший смысл. Когда в перечислении будет дробная часть!?!?! – ErikE 20 July 2015 в 21:18
  • 5
    По крайней мере, это гарантирует, что соответствующие константы перечисления имеют одинаковое целочисленное значение. В конце концов, перечисления представляют собой всего лишь набор целочисленных констант. C # не гарантирует, что назначенные значения являются константами enum vaild, т. Е. Перечисления не являются безопасными по типу в строгом смысле. – Olivier Jacot-Descombes 2 February 2018 в 17:23

Решения, вышеперечисленные с использованием классов с 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) { /* ... */ }
    // ...
}
83
ответ дан Ian Kemp 23 August 2018 в 02:30
поделиться
  • 1
    Я думаю, что это правильный ответ. Вы не можете наследовать для Enum, но это может позволить вам управлять им! – robob 1 February 2011 в 08:10
  • 2
    Хороший и чистый. +1. Просто подсказка, вам действительно не нужна стоимость int вообще. – Ignacio Soler Garcia 14 June 2012 в 08:35
  • 3
    @SoMoS, вы предполагаете, что object используется , как рекомендуется для lock ? Я не могу придумать хороший способ сделать эту идею совместимой с частью «нумерации» enum, где MyEnum.First < MyEnum.Second. Но если упорядочение не важно для вашего enum, то это будет отличный способ избежать магических int-констант. – binki 1 April 2014 в 18:05
  • 4
    Я никогда не думал о перечислениях, как будто вы можете сказать FileOpenMode.Read & gt; FileOpenMode.Write. Если это так, вам нужен int или даже лучший IEqualityComparer для этого перечисления. С уважением. – Ignacio Soler Garcia 1 April 2014 в 21:49
  • 5
    @binki, используя случайные object, сделает значения разными для каждого экземпляра сборки, чтобы они не могли быть сериализованы. – ivan_pozdeev 16 December 2014 в 10:20

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

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

1
ответ дан Jaider 23 August 2018 в 02:30
поделиться

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

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

1
ответ дан Jeroen Landheer 23 August 2018 в 02:30
поделиться
  • 1
    Это немного некорректно из-за синтаксиса c #, но перечисление не может фактически наследовать от int, uint и т. Д. Под капотом они все еще наследуются от System.Enum. Просто член, представляющий перечисление, вводится в int, uint и т. Д. ... – JaredPar 16 April 2009 в 21:12
  • 2
    @JaredPar. Когда перечисление перегружено из uint, это означает, что все значения являются значениями uint и т. Д. По умолчанию enum наследует int. (Взгляните на спецификацию C #, enum SomeEnum: uint {...} действительно работает.) – Jeroen Landheer 17 April 2009 в 03:09
  • 3
    Вообще-то, нет. Он наследует System.enum. Как было опубликовано раньше и чаще здесь, то, что вы считаете наследством, - это просто dangambiguity в csharp. – TomTom 28 October 2010 в 13:22

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

1
ответ дан Lucero 23 August 2018 в 02:30
поделиться

Вы можете достичь того, чего хотите с помощью классов:

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;
145
ответ дан M4N 23 August 2018 в 02:30
поделиться
  • 1
    Как вы перечислите поля в этом классе? Для меня это важное поведение перечисления: Enum.GetValues(typeof(MyEnum) – Mike de Klerk 3 June 2015 в 07:46
  • 2
    Вы можете использовать отражение: void Test() { foreach (System.Reflection.PropertyInfo pi in typeof(Consume).GetProperties()) { Console.WriteLine(pi.Name); } } – mnieto 5 June 2015 в 10:08
  • 3
    Вам нужно убедиться, что сбор свойств с отражением игнорирует унаследованные свойства Object. – Robert 17 July 2015 в 14:11
  • 4
    Отражение для этой задачи очень уродливое. – Dylan Hunt 31 July 2018 в 07:00
  • 5
    Хороший трюк, особенно при работе с перечислением, которые недоступны из интерфейса для моделей на уровне доступа к данным. – Luther 4 August 2018 в 11:02

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

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

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

re все тот же базовый тип (т. е. int) вы можете назначить значение из экземпляра одного типа другому, отличному от другого. Не идеально, но он работает.

12
ответ дан Pascal 23 August 2018 в 02:30
поделиться
  • 1
    база зарезервирована, но база не – erikkallen 16 April 2009 в 21:02
  • 2
    Он имеет в виду использование OP базы в качестве имени перечисления, это всего лишь примерное имя, которое я уверен – John Rasch 16 April 2009 в 22:39
  • 3
    То же, что и ответ Генисио. – nawfal 3 July 2014 в 07:50

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

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

1
ответ дан ShloEmi 23 August 2018 в 02:30
поделиться

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

Например,

public enum Car:int{
Toyota,
Benz,
}
-6
ответ дан Sriwantha Attanayake 23 August 2018 в 02:30
поделиться
  • 1
    это не наследование в типичном смысле .. – nawfal 16 February 2013 в 17:06
  • 2
    Я думаю, что OP просил наследовать одно перечисление от другого, а не только из базового числового типа (который все перечисления делают на C #, либо неявно, либо явно). – reirab 6 September 2013 в 20:46

Я также хотел перегрузить 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
}
0
ответ дан Tobias Knauss 23 August 2018 в 02:30
поделиться

Это то, что я сделал. То, что я сделал по-другому, использует одно и то же имя и ключевое слово 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
    }
}
3
ответ дан toddmo 23 August 2018 в 02:30
поделиться
  • 1
    Рассмотрите возможность установки другого диапазона для новых значений в производном классе (например, SmallMedium = 100,), чтобы вы могли поддерживать совместимость со старыми версиями своего программного обеспечения при добавлении новых значений в базовый класс. Например, добавление размера Huge в вашем базовом enum присваивает 4 его значение, но 4 уже используется SmallMedium в производном классе. – Roberto 2 April 2017 в 10:49
  • 2
    @Roberto, Чтобы решить эту проблему, я никогда не сохраняю значения enum, а только имена. И соблюдение их синхронизации является обязательным условием здесь. Поэтому добавление Huge в базовый класс потребует Huge в подклассе перед SmallMedium – toddmo 2 April 2017 в 21:36
  • 3
    BaseBall не может быть самым умным именем здесь. Это сбивает с толку. Так как бейсбол на самом деле вещь. Если вы пропустите, что это всего лишь базовый класс для мяча, выглядит странно, что волейбол наследуется от физически меньшего бейсбола :). Мое предложение - просто использовать Ball – Nick N. 12 March 2018 в 11:25

Я знаю, что этот ответ довольно поздний, но это то, что я закончил:

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)
{
}
6
ответ дан Wai Ha Lee 23 August 2018 в 02:30
поделиться
  • 1
    Магические числа могут быть заменены объектами в соответствии с stackoverflow.com/questions/757684/… . Но для этого конкретного случая вы можете получить лучшее из обоих миров, используя особенности существующей биологической номенклатуры, чтобы гарантировать уникальность. – ivan_pozdeev 16 December 2014 в 10:17
Другие вопросы по тегам:

Похожие вопросы: