有没有办法来检查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); } }