您可以在运行时添加到枚举类型

时间:2010-05-06 08:53:51

标签: c# enums runtime

如果我有枚举类型:

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

我可以在运行时以某种方式添加:

PingPong = 4

6 个答案:

答案 0 :(得分:30)

枚举有一个后备存储,如果你没有指定它,则默认为int。可以直接指定定义值之外的值:

Sport pingPong = (Sport)4;

然后你可以检查它:

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

这就是为什么你有静态函数Enum.IsDefined()来检查实际值是否在预期值内。请注意,该函数不适用于复合标志值。

bool isValueDefined = Enum.IsDefined(typeof(Sport), value);
编辑:在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"
    }
}

答案 1 :(得分:10)

这是更多面向对象的方式,可能实现您想要实现的目标。这个解决方案的灵感来自早期的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 bock - 即使结构可转换为int

  4. implicit operator Sport可以为结构分配整数值,即Sport rugby = 5

  5. const值是编译时已知的运动。它们也可以用作case标签。

  6. 我实际上会做什么

    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类中处理。

    编辑认识到我的相等运算符将抛出StackOverflowException我总是忘记写Object.ReferenceEquals(obj,null)而不是obj==null,这将无限递归。

答案 2 :(得分:5)

不,您无法在运行时修改类型。您可以发出新类型,但无法修改现有类型。

答案 3 :(得分:2)

是的,您可以使用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();

在我看来,拥有枚举值比字典或基于列表的解决方案更可取,因为使用更少的内存和没有线程副作用。

Here are some samples了解如何在重新启动应用程序时加载生成的枚举。我建议你选择一个适合你的方法,你可以使用System.AddIn命名空间提供的方法,或者使用你的IoC。

当我需要为机器学习生成数据时,我使用这个,因为它具有Enum值,并且优于查找表(在内存或数据库中),因为我没有这些大数据集可用的IO。

答案 4 :(得分:0)

如果可以在程序启动时定义枚举,则将枚举放在单独的程序集中,并使用将重新编译枚举的引导程序,覆盖旧版本,然后启动实际的应用程序。这不是最干净的方法,但它确实有效。

答案 5 :(得分:0)

我会创建一个完整的枚举,其中包含您需要的所有值,例如

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

然后保留无效运动条目的列表,因此该列表最初将包含PingPongRugby。每次访问枚举时,也请检查您的无效运动列表。

然后您可以在任何阶段调整您的无效运动列表以适应