Можете ли вы добавить тип enum во время выполнения - PullRequest
29 голосов
/ 06 мая 2010

Если у меня есть тип enum:

Public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
}

Можно ли как-то добавить во время выполнения:

PingPong = 4

Ответы [ 6 ]

31 голосов
/ 06 мая 2010

У перечисления есть резервное хранилище, по умолчанию int, если вы его не указали. Можно напрямую назначать значения за пределами определенных значений:

Sport pingPong = (Sport)4;

Тогда вы можете проверить это:

if (value == (Sport)4) {}

Именно поэтому у вас есть статическая функция Enum.IsDefined() для проверки того, попадает ли действительное значение в ожидаемые значения. Обратите внимание, что функция не работает для значений составного флага.

bool isValueDefined = Enum.IsDefined(typeof(Sport), value);

РЕДАКТИРОВАТЬ: После комментария Ганса Пассанта: Вам не нужно использовать буквальное значение 4. Вы можете использовать все, что возвращает int. Например:

Dictionary<int, string> AdditionalSports = new Dictionary<int, string>();
AdditionalSports.Add(4, "PingPong");

// Usages: if
if (AdditionalSports.ContainsKey(value))
{
    // Maybe do something with AdditionalSports[value], i.e. "PingPong"
}

// In a switch:
switch (value)
{
case default:
    // Since it won't be found in the enum-defined values
    if (AdditionalSports.ContainsKey(value))
    {
        // Maybe do something with AdditionalSports[value], i.e. "PingPong"
    }
}
11 голосов
/ 11 мая 2010

Вот более объектно-ориентированный способ, возможно, достичь того, чего вы пытаетесь достичь. Это решение основано на раннем Java-подходе к перечислению:

struct Sport {
    readonly int value;
    public Sport(int value) {
        this.value = value;
    }
    public static implicit operator int(Sport sport) {
        return sport.value;
    }
    public static implicit operator Sport(int sport) {
        return new Sport(sport);
    }

    public const int Tennis =       0;
    public const int Football =     1;
    public const int Squash =       2;
    public const int Volleyball =   3;
}

//Usage:
Sport sport = Sport.Volleyball;
switch(sport) {
    case Sport.Squash:
        Console.WriteLine("I bounce really high");
        break;
}
Sport rugby = 5;
if (sport == rugby)
    Console.WriteLine("I am really big and eat a lot");

Чтобы пройти через различные возможности этого решения.

  1. Это неизменяемая структура, которая заключает в себе целочисленное значение. Значение принудительно устанавливается неизменным по ключевому слову readonly.

  2. Единственный способ создать одну из этих структур - вызвать конструктор, который принимает значение в качестве параметра.

  3. implicit operator int существует для того, чтобы структуру можно было использовать в блоке switch - то есть, чтобы сделать структуру конвертируемой в int.

  4. implicit operator Sport существует для того, чтобы вы могли присваивать целочисленные значения структуре, т.е. Sport rugby = 5.

  5. const - это виды спорта, известные во время компиляции. Их также можно использовать как метки case.

Что бы я на самом деле делал

public static class Sports {
    public static readonly Sport Football = new Sport("Football");
    public static readonly Sport Tennis = new Sport("Tennis");
}

public class Sport {
    public Sport(string name) {
        Name = name;
    }
    public string Name { get; private set; }

    // override object.Equals
    public override bool Equals(object obj) {
        var other = obj as Sport;
        if(other == null) {
            return false;
        }

        return other == this;
    }

    // override object.GetHashCode
    public override int GetHashCode() {
        return Name.GetHashCode();
    }

    public static bool operator == (Sport sport1, Sport sport2) {
        if(Object.ReferenceEquals(sport1, null) && Object.ReferenceEquals(sport2 , null))
            return true;

        if(Object.ReferenceEquals(sport1, null) || Object.ReferenceEquals(sport2, null))
            return false;

        return sport1.Name == sport2.Name;
    }
    public static bool operator !=(Sport sport1, Sport sport2) {
        return !(sport1 == sport2);
    }
}

Это создаст класс значений Sport с именем. В зависимости от вашего приложения вы можете расширить этот класс для предоставления других атрибутов и методов. Наличие этого класса дает вам больше гибкости, так как вы можете создавать его подклассы.

Sports класс предоставляет статическую коллекцию видов спорта, которые известны во время компиляции. Это похоже на то, как некоторые платформы .NET обрабатывают именованные цвета (например, WPF). Вот использование:

List<Sport> sports = new List<Sport>();

sports.Add(Sports.Football);
sports.Add(Sports.Tennis);
//What if the name contains spaces?
sports.Add(new Sport("Water Polo"));

var otherSport = new Sport("Other sport");

if(sports.Contains(otherSport)) {
    //Do something
}

foreach(var sport in sports) {
    if(sport == otherSport) {
        //Do Something
    } else if(sport == Sports.Football) {
        //do something else
    }
}

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

EDIT Понял, что мой оператор равенства выдает StackOverflowException Я всегда забываю написать Object.ReferenceEquals(obj,null) вместо obj==null, что будет повторяться бесконечно.

5 голосов
/ 06 мая 2010

Нет, вы не можете изменять типы во время выполнения. Вы можете создавать новые типы, но изменение существующих невозможно.

2 голосов

Ну да, вы можете создавать и изменять Enum во время выполнения, используя класс EnumBuilder , см. Пример в MSDN

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
EnumBuilder eb = mb.DefineEnum("Elevation", TypeAttributes.Public, typeof(int));
eb.DefineLiteral("Low", 0);
eb.DefineLiteral("High", 1);
Type finished = eb.CreateType();

Наличие значения enum в моеммнение, предпочтительнее словарей или решений на основе списков, так как используется меньше памяти и нет побочных эффектов потока.

Вот несколько примеров о том, как загрузить сгенерированное перечисление при перезапуске приложения.Я предлагаю вам выбрать тот, который работает для вас, вы можете использовать методы, предоставленные пространством имен System.AddIn , как я думаю ... или использовать ваш IoC.

Я использую это, когда янеобходимо сгенерировать данные для машинного обучения как имеющие значение Enum и предпочтительнее, чем таблицы поиска (в памяти или базе данных), поскольку у меня просто нет ввода-вывода с этими большими наборами данных.

0 голосов
/ 04 ноября 2017

Я бы создал полный Enum со всеми значениями, которые вам когда-либо понадобятся, например:

Public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
    PingPong = 4;
    Rugby = 5;  // for example
}

А затем сохраните СПИСОК записей о недопустимых видах спорта, чтобы список изначально содержал PingPong и Rugby. Каждый раз, когда вы получаете доступ к Enum, также проверяйте его в списке Invalid Sports.

Тогда вы можете просто настроить свой Invalid Sports List, чтобы он подходил на любом этапе

0 голосов
/ 11 мая 2010

Если перечисление может быть определено при запуске программы, вы помещаете перечисление в отдельную сборку и используете загрузчик, который перекомпилирует перечисление, перезаписывает старую версию и затем запускает реальное приложение. Это не самый чистый метод, но он работает.

...