有没有办法检查 int 是否是 C# 中的合法枚举?
我读过一些 SO 帖子,似乎缺少最基本的操作。
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
。有没有办法验证进入枚举的值?
I've read a few SO posts and it seems most basic operation is missing.
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());
}
This causes no exceptions, it's happy to store 78
. Is there a way to validate a value going into an enum?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
上述解决方案不处理
[Flags]
情况。我下面的解决方案可能存在一些性能问题(我确信可以通过各种方式进行优化),但本质上它将始终证明枚举值是否有效。
它依赖于三个假设:
int
,绝对不能为其他-
如果没有匹配的枚举(标志或不),则对枚举调用
ToString()
会返回int
值。如果匹配允许的枚举值,它将打印匹配的名称。因此:
考虑到这两条规则,我们可以假设,如果 .NET Framework 正确完成其工作,则对有效枚举的
ToString()
方法的任何调用都将产生以字母字符作为其值的内容。第一个字符:人们可以称其为“hack”,但优点是,通过依赖 Microsoft 自己的
Enum
和 C# 标准实现,您不必依赖自己的可能有错误的代码或检查。在性能不是特别关键的情况下,这将节省大量令人讨厌的switch
语句或其他检查!编辑
感谢@ChaseMedallion 指出我最初的实现不支持负值。此问题已得到纠正并提供了测试。
以及支持它的测试:
The above solutions do not deal with
[Flags]
situations.My solution below may have some performance issues (I'm sure one could optimise in various ways) but essentially it will always prove whether an enum value is valid or not.
It relies on three assumptions:
int
, absolutely nothing else-
Calling
ToString()
on an enum returns either theint
value if no enum (flag or not) is matched. If an allowed enum value is matched, it will print the name of the match(es).So:
With these two rules in mind we can assume that if the .NET Framework does its job correctly that any calls to a valid enum's
ToString()
method will result in something that has an alphabetic character as its first character:One could call it a "hack", but the advantages are that by relying on Microsoft's own implementation of
Enum
and C# standards, you're not relying on your own potentially buggy code or checks. In situations where performance is not exceptionally critical, this will save a lot of nastyswitch
statements or other checks!Edit
Thanks to @ChaseMedallion for pointing out that my original implementation did not support negative values. This has been remedied and tests provided.
And the tests to back it up:
规范的答案是 Enum.IsDefined,但这 a:如果在紧密循环中使用,有点慢,b:对于
[Flags]
枚举没有用。就我个人而言,我不会再担心这一点,只需适当地
切换
,记住:默认值:
(或者有一个空的default:
解释原因)default:
像这样:
The canonical answer would be
Enum.IsDefined
, but that is a: a bit slow if used in a tight loop, and b: not useful for[Flags]
enums.Personally, I'd stop worrying about that, and just
switch
appropriately, remembering:default:
(or have an emptydefault:
explaining why)default:
Like so:
使用:
Use:
使用Enum.IsDefined。
Use Enum.IsDefined.
为了处理
[Flags]
您还可以使用 C# Cookbook 中的此解决方案:首先,向枚举添加一个新的
ALL
值:然后,检查该值是否在
ALL
中:In order to deal with
[Flags]
you can also use this solution from C# Cookbook:First, add a new
ALL
value to your enum:Then, check if the value is in
ALL
:正如其他人所说,即使您具有使用
FlagsAttribute
装饰的枚举的有效位标志组合,Enum.IsDefined
也会返回false
。遗憾的是,创建对 有效 位标志返回 true 的方法的唯一方法有点冗长:
您可能希望将
GetCustomAttribute
的结果缓存在字典中:请注意上面的代码对
T
使用了新的Enum
约束,该约束仅自 C# 7.3 起可用。在旧版本中,您需要传递一个对象值
并对其调用GetType()
。As the others said,
Enum.IsDefined
returnsfalse
even if you have a valid combination of bit flags for an enum decorated with theFlagsAttribute
.Sadly, the only way to create a method returning true for valid bit flags is a bit lengthy:
You may want to cache the results of
GetCustomAttribute
in a dictionary:Note that the code above uses the new
Enum
constraint onT
which is only available since C# 7.3. You need to pass anobject value
in older versions and callGetType()
on it.一种方法是依靠强制转换和枚举到字符串的转换。将 int 转换为 Enum 类型时,int 会转换为相应的枚举值,或者如果未为 int 定义枚举值,则生成的枚举仅包含 int 作为值。
未测试任何边缘情况。
One way to do would be to rely on casting and enum to string conversion. When casting int to an Enum type the int is either converted to a corresponding enum value or the resulting enum just contains int as a value if enum value is not defined for the int.
Not tested for any edge cases.
我知道这是一个老问题,但我今天遇到了这个问题,我想扩展 Josh Comley 的答案(https://stackoverflow .com/a/23177585/3403999)
我想解决乔什的回答中有几个错误的假设:
enum MyEnum { _One = 1 }
有效。无论如何,这是我更新的解决方案:
我确实发现您可以在枚举成员名称中使用其他语言的 Unicode 字母(https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/identifier-names< /a>)。我的解决方案在这方面仍然通过。我使用以下枚举进行了测试:
enum MyEnum { \u05D0 }
。枚举已编译,IsValid 返回 true。我很好奇,与使用带有填充
Enum.GetValues(typeof(TEnum))
的 HashSet 的静态帮助程序类相比,您会采取这种路线,在其中检查是否会受到什么样的性能影响HashSet 包含枚举值。我们的想法是,Enum.GetValues 和 Enum.IsDefined 都只是昂贵的反射命中的包装,因此您可以使用 GetValues 执行一次反射,缓存结果,然后只需检查 HashSet。我使用 StopWatch 和 Random 进行了一个相当简单的测试,它将生成有效的 &无效的枚举值,然后我通过 3 种不同的方法运行它们:ToString 方法、GetValues HashSet 方法和 IsDefined 方法。我让他们对每个方法执行 int.MaxValue 次。结果:
因此,如果性能是一个问题,或者您正在执行循环,那么所有推荐 IsDefined 的解决方案可能都是一个坏主意。如果您只是使用它以某种方式验证单个实例上的用户输入,那么它可能并不重要。
对于 HashSet,对于您运行的每个不同的枚举来说,性能都会受到影响(因为第一次运行新的枚举类型会生成一个新的静态 HashSet)。不科学,但在我的 PC 上,在开始使用 ToString 方法执行单个枚举之前,我的盈亏平衡点似乎约为 200k 到 300k 运行。
ToString 方法虽然不是最快的方法,但具有处理 IsDefined 和 HashSet 都无法容纳的 Flags 枚举的额外好处。
如果性能确实是一个问题,请不要使用这 3 种方法中的任何一种。相反,编写一个方法来验证针对该枚举优化的特定枚举。
另请注意,我的测试使用相对较小的枚举(5 个左右的元素)。一旦你开始使用更大的枚举,我不知道 ToString 与 HashSet 之间的性能如何。
I know this is an old question, but I ran into this today, and I wanted to expand on Josh Comley's answer (https://stackoverflow.com/a/23177585/3403999)
There's a couple of wrong assumptions in Josh's answer that I wanted to address:
enum MyEnum { _One = 1 }
is valid.Anyway, here's my updated solution:
I did discover that you can use Unicode letters from other languages in enum member names (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/identifier-names). My solution still passes in this regard. I tested with the following enum:
enum MyEnum { \u05D0 }
. The enum compiled, and the IsValid returned true.I was curious what kind of performance hit you'd take going this route vs using a static helper class with a HashSet that is filled with
Enum.GetValues(typeof(TEnum))
where you check to see if the HashSet contains the enum value. The thought being that both Enum.GetValues and Enum.IsDefined are just wrappers around expensive Reflection hits, so you do the Reflection once with GetValues, cache the results, and then just check the HashSet going forward.I ran a fairly simple test with a StopWatch and Random that would generate valid & invalid enum values, and then I ran them through 3 different methods: the ToString method, the GetValues HashSet method, and the IsDefined method. I had them do each method int.MaxValue times. The results:
So all the solutions recommending IsDefined are probably a bad idea if performance is a concern, or your doing a loop. If you are only using it somehow validate user input on single instances, it probably doesn't matter.
For the HashSet, it's a small performance hit for each different enum you run through it (cause the first time a new enum type gets ran through generates a new static HashSet). Not scientific, but it seemed my break even point on my PC was about 200k to 300k runs for a single enum before it started out performing using the ToString method.
The ToString method, while not the fastest had the added benefit of handling Flags enums that neither the IsDefined nor HashSet accommodate.
If performance really is a concern, don't use any of these 3 methods. Instead write a method that validates on a specific enum optimized to that enum.
Also note that my tests were with relatively small enums (5 or so elements). I don't know how performance between ToString vs HashSet once you start getting into larger enums.
对一个老问题的另一个答案,源于以下事实:a)如果您有一个 int 对于您的枚举来说不是有效值,并且您尝试将其转换为该枚举类型,它将无论如何强制转换它,但结果值将与您定义的任何枚举值都不匹配,并且 b) 有一个通用版本的
Enum.IsDefined
,它将根据您传递给它的内容推断出类型。这是一个利用上述内容的通用 TryGetEnumValue 示例。一些测试显示了它的工作效果:
根据本线程中的其他讨论,此方法不会识别复合 Flags 值。
Another answer to an old question, arising out of the facts that a) if you have an int that is not a valid value for your enum, and you try to cast it to that enum type, it will cast it anyway but the resulting value will not match any of your defined enum values, and b) there is a generic version of
Enum.IsDefined
which will infer the type from what you pass to it. Here's a generic TryGetEnumValue example making use of the above.And some tests showing it at work:
As per other discussions in this thread, this method won't recognise composite Flags values.
查看 Enum.IsDefined
用法:
这是示例从该页面:
该示例显示以下输出:
Check out Enum.IsDefined
Usage:
This is the example from that page:
The example displays the following output: