如果我有枚举类型:
Public enum Sport
{
Tennis = 0;
Football = 1;
Squash = 2;
Volleyball = 3;
}
我可以在运行时以某种方式添加:
PingPong = 4
答案 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");
了解此解决方案的不同特点。
这是一个包含整数值的不可变结构。该值由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
类中处理。
编辑认识到我的相等运算符将抛出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
}
然后保留无效运动条目的列表,因此该列表最初将包含PingPong
和Rugby
。每次访问枚举时,也请检查您的无效运动列表。
然后您可以在任何阶段调整您的无效运动列表以适应