Подтвердить что ты не робот

Можете ли вы добавить к типу перечисления во время выполнения

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

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

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

PingPong = 4
4b9b3361

Ответ 1

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

Sport pingPong = (Sport)4;

Затем вы можете проверить его:

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

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

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

EDIT: После комментария Hans Passant: вам не нужно использовать буквальное значение 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"
    }
}

Ответ 2

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

Пройти различные действия этого решения.

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

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

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

  • implicit operator Sport существует так, что вы можете назначить целочисленные значения для структуры, то есть Sport rugby = 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, который будет бесконечно рекурсивно.

Ответ 3

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

Ответ 4

Это интересный вопрос, но рассмотрели ли вы другой подход к своей проблеме? Возьмите комментарий @fearofawhackplanet, например...

var Sports = new Dictionary<string, int>
{
    {"Tennis", 0},
    {"Football ", 1},
    {"Squash ", 2},
    {"Volleyball ", 3},
};

Затем позже Sports["PingPong"] = 4;

Извините, у меня нет прямого ответа на заданный вами вопрос, но, возможно, это может решить вашу проблему по-другому.

Ответ 5

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

Ответ 6

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

Ответ 7

Я бы создал полный 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.

Затем вы можете просто настроить свой список запрещенных видов спорта на любой стадии