it-swarm.com.ru

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

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

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

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

PingPong = 4
25
CJ7

У enum есть резервное хранилище, по умолчанию 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"
    }
}
25
Daniel Rose

Вот более объектно-ориентированный способ, возможно, достичь того, чего вы пытаетесь достичь. Это решение основано на раннем 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, который будет повторяться бесконечно.

9
Igor Zevaka

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

5
Darin Dimitrov

Да, вы можете создавать и/или изменять 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 и предпочтительнее, чем таблицы поиска (в памяти базы данных), поскольку у меня просто нет IO, доступного с этими большими наборами данных.

Удачного кодирования

Вальтер

2

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

0
Stephan

Я бы создал полный Enum со всеми необходимыми вам значениями

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

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

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

0
Grantly