有没有办法来检查int是否合法枚举在C#中?

我读过几个SOpost,似乎最基本的操作是失踪。

public enum LoggingLevel { Off = 0, Error = 1, Warning = 2, Info = 3, Debug = 4, Trace = 5 }; if (s == "LogLevel") { _log.LogLevel = (LoggingLevel)Convert.ToInt32("78"); _log.LogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), "78"); _log.WriteDebug(_log.LogLevel.ToString()); } 

这不会导致例外,所以很高兴能存储78 。 有没有办法来validation一个值进入枚举?

查看Enum.IsDefined

用法:

 if(Enum.IsDefined(typeof(MyEnum), value)) MyEnum a = (MyEnum)value; 

这是该页面中的示例:

 using System; [Flags] public enum PetType { None = 0, Dog = 1, Cat = 2, Rodent = 4, Bird = 8, Reptile = 16, Other = 32 }; public class Example { public static void Main() { object value; // Call IsDefined with underlying integral value of member. value = 1; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); // Call IsDefined with invalid underlying integral value. value = 64; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); // Call IsDefined with string containing member name. value = "Rodent"; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); // Call IsDefined with a variable of type PetType. value = PetType.Dog; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); value = PetType.Dog | PetType.Cat; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); // Call IsDefined with uppercase member name. value = "None"; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); value = "NONE"; Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value)); // Call IsDefined with combined value value = PetType.Dog | PetType.Bird; Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value)); value = value.ToString(); Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value)); } } 

该示例显示以下输出:

 // 1: True // 64: False // Rodent: True // Dog: True // Dog, Cat: False // None: True // NONE: False // 9: False // Dog, Bird: False 

上述解决scheme不涉及[Flags]情况。

我的下面的解决scheme可能有一些性能问题(我敢肯定,可以以各种方式进行优化),但本质上它将永远certificate枚举值是否有效

它依赖于三个假设:

  • 在C#中的枚举值只允许为int ,绝对没有别的
  • C#中的枚举名称必须以字母字符开头
  • 没有有效的枚举名可以带有减号:

在枚举上调用ToString() ,如果没有枚举(标志或不匹配),则返回int值。 如果允许的枚举值匹配,它将打印匹配的名称。

所以:

 [Flags] enum WithFlags { First = 1, Second = 2, Third = 4, Fourth = 8 } ((WithFlags)2).ToString() ==> "Second" ((WithFlags)(2 + 4)).ToString() ==> "Second, Third" ((WithFlags)20).ToString() ==> "20" 

考虑到这两条规则,我们可以假设,如果.NET框架正确地执行了任何对有效枚举的ToString()方法的调用,都将导致某些字母为第一个字符:

 public static bool IsValid<TEnum>(this TEnum enumValue) where TEnum : struct { var firstChar = enumValue.ToString()[0]; return (firstChar < '0' || firstChar > '9') && firstChar != '-'; } 

人们可以把它称为“破解”,但优点是依靠微软自己的Enum和C#标准实现,而不是依靠自己的潜在错误代码或检查。 在性能不是特别重要的情况下,这将节省大量讨厌的switch语句或其他检查!

编辑

感谢@ChaseMedallion指出我原来的实现不支持负值。 这已经解决了,并提供了testing。

和testing支持:

 [TestClass] public class EnumExtensionsTests { [Flags] enum WithFlags { First = 1, Second = 2, Third = 4, Fourth = 8 } enum WithoutFlags { First = 1, Second = 22, Third = 55, Fourth = 13, Fifth = 127 } enum WithoutNumbers { First, // 1 Second, // 2 Third, // 3 Fourth // 4 } enum WithoutFirstNumberAssigned { First = 7, Second, // 8 Third, // 9 Fourth // 10 } enum WithNagativeNumbers { First = -7, Second = -8, Third = -9, Fourth = -10 } [TestMethod] public void IsValidEnumTests() { Assert.IsTrue(((WithFlags)(1 | 4)).IsValid()); Assert.IsTrue(((WithFlags)(1 | 4)).IsValid()); Assert.IsTrue(((WithFlags)(1 | 4 | 2)).IsValid()); Assert.IsTrue(((WithFlags)(2)).IsValid()); Assert.IsTrue(((WithFlags)(3)).IsValid()); Assert.IsTrue(((WithFlags)(1 + 2 + 4 + 8)).IsValid()); Assert.IsFalse(((WithFlags)(16)).IsValid()); Assert.IsFalse(((WithFlags)(17)).IsValid()); Assert.IsFalse(((WithFlags)(18)).IsValid()); Assert.IsFalse(((WithFlags)(0)).IsValid()); Assert.IsTrue(((WithoutFlags)1).IsValid()); Assert.IsTrue(((WithoutFlags)22).IsValid()); Assert.IsTrue(((WithoutFlags)(53 | 6)).IsValid()); // Will end up being Third Assert.IsTrue(((WithoutFlags)(22 | 25 | 99)).IsValid()); // Will end up being Fifth Assert.IsTrue(((WithoutFlags)55).IsValid()); Assert.IsTrue(((WithoutFlags)127).IsValid()); Assert.IsFalse(((WithoutFlags)48).IsValid()); Assert.IsFalse(((WithoutFlags)50).IsValid()); Assert.IsFalse(((WithoutFlags)(1 | 22)).IsValid()); Assert.IsFalse(((WithoutFlags)(9 | 27 | 4)).IsValid()); Assert.IsTrue(((WithoutNumbers)0).IsValid()); Assert.IsTrue(((WithoutNumbers)1).IsValid()); Assert.IsTrue(((WithoutNumbers)2).IsValid()); Assert.IsTrue(((WithoutNumbers)3).IsValid()); Assert.IsTrue(((WithoutNumbers)(1 | 2)).IsValid()); // Will end up being Third Assert.IsTrue(((WithoutNumbers)(1 + 2)).IsValid()); // Will end up being Third Assert.IsFalse(((WithoutNumbers)4).IsValid()); Assert.IsFalse(((WithoutNumbers)5).IsValid()); Assert.IsFalse(((WithoutNumbers)25).IsValid()); Assert.IsFalse(((WithoutNumbers)(1 + 2 + 3)).IsValid()); Assert.IsTrue(((WithoutFirstNumberAssigned)7).IsValid()); Assert.IsTrue(((WithoutFirstNumberAssigned)8).IsValid()); Assert.IsTrue(((WithoutFirstNumberAssigned)9).IsValid()); Assert.IsTrue(((WithoutFirstNumberAssigned)10).IsValid()); Assert.IsFalse(((WithoutFirstNumberAssigned)11).IsValid()); Assert.IsFalse(((WithoutFirstNumberAssigned)6).IsValid()); Assert.IsFalse(((WithoutFirstNumberAssigned)(7 | 9)).IsValid()); Assert.IsFalse(((WithoutFirstNumberAssigned)(8 + 10)).IsValid()); Assert.IsTrue(((WithNagativeNumbers)(-7)).IsValid()); Assert.IsTrue(((WithNagativeNumbers)(-8)).IsValid()); Assert.IsTrue(((WithNagativeNumbers)(-9)).IsValid()); Assert.IsTrue(((WithNagativeNumbers)(-10)).IsValid()); Assert.IsFalse(((WithNagativeNumbers)(-11)).IsValid()); Assert.IsFalse(((WithNagativeNumbers)(7)).IsValid()); Assert.IsFalse(((WithNagativeNumbers)(8)).IsValid()); } } 

规范的答案是Enum.IsDefined ,但是这是一个有点慢,如果用在一个紧密的循环,b:对[Flags]枚举没有用。

就我个人而言,我会不再担心这个问题,只要适当地switch ,记住:

  • 如果可以不识别所有东西(只是不做任何事情),那么不要添加一个default:或者有一个空的default:解释原因)
  • 如果有一个合理的默认行为,把它放在default:
  • 否则,处理你所知道的,并抛出一个例外:

像这样:

 switch(someflag) { case TriBool.Yes: DoSomething(); break; case TriBool.No: DoSomethingElse(); break; case TriBool.FileNotFound: DoSomethingOther(); break; default: throw new ArgumentOutOfRangeException("someflag"); } 

使用Enum.IsDefined 。

使用:

 Enum.IsDefined ( typeof ( Enum ), EnumValue ); 

一种方法是依靠铸造和枚举来进行string转换。 当将int转换为Enumtypes时,如果int未定义为枚举值,则将int转换为相应的枚举值,或者生成的枚举只包含int作为值。

 enum NetworkStatus{ Unknown=0, Active, Slow } int statusCode=2; NetworkStatus netStatus = (NetworkStatus) statusCode; bool isDefined = netStatus.ToString() != statusCode.ToString(); 

没有testing任何边缘情况。

为了处理[Flags]你也可以使用C#Cookbook的这个解决scheme :

首先,为你的枚举添加一个新的ALL值:

 [Flags] enum Language { CSharp = 1, VBNET = 2, VB6 = 4, All = (CSharp | VBNET | VB6) } 

然后,检查值是否在ALL

 public bool HandleFlagsEnum(Language language) { if ((language & Language.All) == language) { return (true); } else { return (false); } }