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

Если у меня есть перечислимый тип:

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

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

PingPong = 4
31
задан VBorisoff 19 July 2019 в 12:32
поделиться

4 ответа

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

Sport pingPong = (Sport)4;

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

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

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

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

EDIT: После комментария Ханса Пассанта: Вам не обязательно использовать литеральное значение 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"
    }
}
32
ответ дан 27 November 2019 в 22:24
поделиться

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

5
ответ дан 27 November 2019 в 22:24
поделиться

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

0
ответ дан 27 November 2019 в 22:24
поделиться

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

  4. неявный оператор 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, который будет перебирать бесконечно.

11
ответ дан 27 November 2019 в 22:24
поделиться
Другие вопросы по тегам:

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