Enum "Наследование" - PullRequest
       33

Enum "Наследование"

357 голосов
/ 16 апреля 2009

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

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

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

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

Мысли

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

Ответы [ 13 ]

433 голосов
/ 16 апреля 2009

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

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

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

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

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;
107 голосов
/ 16 апреля 2009

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

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

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

Чтобы пересечь Основу и Потребление ...

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

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

91 голосов
/ 28 октября 2010

В приведенных выше решениях с использованием классов с 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) { /* ... */ }
    // ...
}
13 голосов
/ 16 апреля 2009

Игнорируя тот факт, что 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), вы можете присвоить значение из экземпляра одного типа другому, который приведен. Не идеально, но это работает.

6 голосов
/ 02 мая 2014

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

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)
{
}
4 голосов
/ 24 февраля 2017

Это то, что я сделал. То, что я сделал по-другому - это использование того же имени и ключевого слова new для «потребляющего» * ​​1002 * Поскольку имя 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
    }
}
1 голос
/ 20 марта 2017

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

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

1 голос
/ 16 ноября 2016

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

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

НТН

1 голос
/ 16 апреля 2009

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

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

...