枚举应该以 0 还是 1 开头?
想象一下我定义了以下枚举:
public enum Status : byte
{
Inactive = 1,
Active = 2,
}
使用枚举的最佳实践是什么?它应该像上面的示例一样以 1
开头,还是以 0
开头(没有显式值),如下所示:
public enum Status : byte
{
Inactive,
Active
}
Imagine I have defined the following Enum:
public enum Status : byte
{
Inactive = 1,
Active = 2,
}
What's the best practice to use enum? Should it start with 1
like the above example or start with 0
(without the explicit values) like this:
public enum Status : byte
{
Inactive,
Active
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
除非您有特定原因要更改它,否则请保留枚举的默认值(从零开始)。
Unless you have a specific reason to change it, leave enums with their default values, which begin at zero.
除非您有充分的理由使用原始值,否则您应该只使用隐式值并通过
Status.Active
和Status.Inactive
引用它们。问题是您可能希望将数据存储在平面文件或数据库中,或者使用其他人创建的平面文件或数据库。如果您自己制作,请使其编号适合 Enum 的用途。
如果数据不是您的,您当然会想要使用原始开发人员用作编号方案的任何数据。
如果您计划将枚举用作一组标志,则有一个值得遵循的简单约定:
值应该是 2 的幂,并且可以使用位移运算来表示。
None
,显然应该是0
,但是All
不太明显-1
。~0
是0
的二进制否定,结果是每个位都设置为1
的数字,代表-1
值。对于复合标志(通常为方便起见而使用),可以使用按位或运算符|
合并其他值。Unless you have a good reason to use the raw values, you should only ever be using implicit values and referencing them with
Status.Active
andStatus.Inactive
.The catch is that you might want to store data in a flat file or DB, or use a flat file or DB that someone else created. If you're making it yourself, make it so the numbering fits what the Enum is used for.
If the data is not yours, of course you're going to want to use whatever the original dev had used as a numbering scheme.
If you're planning on using the Enum as a set of flags, there is a simple convention that's worth following:
Values should be powers of two and can be expressed using bit-shift operations.
None
, obviously should be0
, butAll
is less obviously-1
.~0
is the binary negation of0
and results in a number that has every bit set to1
, which represents a value of-1
. For compound flags (often used for convenience) other values may be merged using the bitwise or operator|
.我想说,这取决于你如何使用它们。对于标记枚举,最好将
None
值设置为 0,如下所示:当您的枚举可能映射到数据库查找表时,我会从 1 开始。它不应该对于专业编写的代码来说很重要,但这提高了可读性。
在其他情况下,我会保持原样,不关心它们是以 0 还是 1 开头。
I would say, it depends on how you use them. For flagging enum it is a good practice to have 0 for
None
value, like that:When your enum is likely to be mapped to a database lookup table, I'd start it with 1. It should not matter much for professionally written code, but this improves readability.
In other cases I'd leave it as it is, giving no care whether they start with 0 or 1.
如果未指定,则从 0 开始编号。
明确这一点很重要,因为枚举通常被序列化并存储为 int,而不是字符串。
对于存储在数据库中的任何枚举,我们总是显式地对选项进行编号,以防止在维护期间发生转移和重新分配。
根据微软的说法,推荐的约定是使用第一个零选项来表示未初始化或最常见的默认值。
下面是从 1 而不是 0 开始编号的快捷方式。
如果您希望设置标志值以便对枚举值使用位运算符,请不要从零值开始编号。
If not specified numbering starts at 0.
It is important to be explicit since enums are often serialized and stored as an int, not a string.
For any enum stored in the database, we always explicitly number the options to prevent shifting and reassignment during maintenance.
According to Microsoft, the recommended convention is use the first zero option to represent an uninitialized or the most common default value.
Below is a shortcut to start numbering at 1 instead of 0.
If you wish to set flag values in order to use bit operators on enum values, don't start numbering at the zero value.
我想说最好的做法是不给它们编号并让它隐式 - 这将从 0 开始。因为它是隐式的,所以它的语言偏好总是很好遵循:)
I'd say best practice is to not number them and let it be implicit - which would start from 0. Since its implicit its the language preference which is always good to follow :)
我会以 0 开始一个布尔类型枚举。
除非“Inative”意味着“Inactive”以外的东西:)
这保留了这些的标准。
I would start a boolean type enum with a 0.
Unless "Inative" means something other than "Inactive" :)
This retains the standard for those.
分配所有从 1 开始的值,并使用 `Nullable{T}` 来表示没有值,除非你不能
欣赏 Microsoft 的 框架指南,但我不同意他们关于枚举实践的观点。我认为枚举的各种用例中有很多微妙之处,这些用例并没有真正在此处得到解决,也没有在此处的其他答案中得到解决。
但是,标志枚举确实需要一个
None = 0
值才能工作。这个答案的其余部分不适用于标志枚举。另外,在继续之前,最好先说明C# 枚举的黄金法则:
枚举是半类型安全的雷区
对于这个答案,我将使用这个假设的枚举:
有不同的方法我们可能会使用这个枚举类型。
情况 1:从数据库查询的数据结构的一部分
情况 2:搜索查询的一部分
情况 3:POST 请求的一部分
这些三个类看起来都一样,但数据来自不同的地方,并且经过不同的验证和处理。
情况1:从数据库查询的数据结构的一部分
我们可以对此数据的有效性做出一些假设,因为它应该在保存之前经过验证。
Name
应该是有效的非空字符串。Type
应该是Basic
或Admin
,绝不能是null
或其他无效值。 (目前,忽略此属性的持久化方式,无论是作为INT
/VARCHAR
/etc。)NullName
或永远无效>输入。如果使用较新的 C# 语言功能,
Name
属性可能会声明为不可为 null (string!Name
),尽管并非所有 ORM 都直接支持这一点,因此您可能需要在查询数据后验证空值。情况 2:搜索查询的一部分
这是客户端请求,因此可能存在无效输入。此外,这些属性应该是可选的,因此客户端可以仅使用他们关心的过滤器进行搜索。
您可能希望使用
Nullable
对于值类型和显式可为 null 的引用类型来建模此类型。您可能想要验证的内容:
Name
是null
或非空字符串。或者,您可以将空或空白视为null
。 (您可能不想验证该值是否是真实的用户名。如果无效,搜索将返回 0 个结果。)Type
是一个有效的枚举值,或者是“no”的某种表示形式筛选”。例如,如果客户端发送 Type = "Superuser",这可能表明客户端存在错误,400 响应会有所帮助。情况 3:POST 请求的一部分
这也是客户端输入,但这些属性不应允许
null
/blank 值,并且会有不同的验证规则。您可能需要验证的事项:
Name
是非null
、非空字符串Name
的长度至少为 X 个字符Name
不包含标点符号或空格Type
是有效值与情况 1 类似,您可能需要使用
string!名称
以更准确地表示您的数据。但是,如果这是从 HTTP 请求解析的,您可能仍然需要显式验证空值,具体取决于您使用的框架。那么,表示“无类型”的最佳方式是什么?
框架指南指出,我们应该向枚举中添加一个元素来表示这一点:
那么这对我们的 3 个用例有何影响?它会影响所有这些,因为它们都使用
UserType
。情况 1:从数据库查询的数据结构的一部分
现在可以使用
Type = UserType.None
创建UserQueryResult
实例。当然,这并不是我们的输入允许的第一个无效状态。
UserQueryResult
已允许Name = ""
,但我们正在添加可能无效的状态。在我们访问 UserQueryResult.Type 的地方,我们应该已经有一种防御方法来处理无效的 UserType 值,因为类型系统允许诸如
(UserType)999 之类的事情
。情况 2:搜索查询的一部分
如果我们坚持使用
Nullable
作为可选属性上的值类型,我们现在有两种方法来表示“不过滤”关于用户类型
”。Type = UserType.None
Type = null
这意味着我们在使用此类型的任何地方都需要一些
&&
或||< /code> 处理这两种情况的逻辑。
如果我们在枚举类型上去掉
Nullable
而将其保留在其他值类型上,那么我们会减少选项的数量,但会有一个更复杂的 API 契约,其中包含多种约定,供我们和客户使用记住。情况 3:POST 请求的一部分
这些类型现在允许对此请求使用
Type = UserType.None
。我们需要添加一个特殊的验证规则来检查这一点。从此更改对这 3 种情况的影响中我们可以看到,我们将有效值列表与“无值”的表示相结合。 “无值”仅对情况 2 有效,但我们强制情况 1 和情况 3 的代码处理额外的“无值”复杂性。
此外,在案例 2 中我们可以看到,我们已经有了一种表示值类型“无值”的通用方法,即
Nullable
。在许多方面,这类似于引用类型的 null 处理,使我们接近于用单一统一的方式来表示所有类型的“无值”,从而减少开发人员的心理负担。结论 1
使用
Nullable
表示“无值”,以保持一致性,这样您就有一个不同的类型来表示“一个从不‘无值’的值”。这就是为什么您不应该添加
None
值。但为什么要显式分配枚举int
值呢?原因 1:未分配的属性具有值
default(T)
对于引用类型,
default(T) == null
。对于值类型,
default(T) == (T)0
。假设客户端想要发布请求来创建新用户。一个好的 JSON 有效负载如下所示:(
为了便于阅读,我们的 JSON 解析器被配置为接受枚举字符串。在 JSON 中使用字符串还是整数作为枚举在这里并不真正相关。)
正如预期的那样,此有效负载将被解析为像这样的 C# 对象:
如果我们的客户端发送不完整的 JSON,会发生什么?
这将再次被解析为
default(UserType) == (UserType)0
。我们的枚举可以通过以下三种方式之一进行声明:
None
开头(None = 0
,或者只是将None
隐式分配给0
)Admin
隐式分配给0
开始Admin = 1
开始在情况 1 中,
Type
被解析为None
。由于None
是我们枚举的一部分,因此我们需要针对这种情况进行验证,以防止将None
保存到数据库中。不过,我已经介绍了不应使用None
值的原因。在情况 2 中,
Type
被解析为Admin
。发生这种情况后,无法区分来自负载中的"Type": "Admin"
的Admin
值与Type<有效负载中缺少 /code>。这显然不好。
在情况 3 中,
Type
被解析为(UserType)0
,它没有名称。这乍一看很奇怪,但实际上是最好的情况。由于枚举允许无效值(例如(UserType)999
),因此我们无论如何都应该针对来自客户端的无效值进行验证。这只会使“未分配”成为无效值而不是有效值。对我来说,情况 3 似乎也与 C# 最近添加的内容非常一致,这些添加内容使得表示无效值变得更加困难:不可为 null 的引用类型和必需的属性。相反,情况 1 感觉像是 C# 1 中泛型和 Nullable之前的遗留模式。
原因 2:避免意外的合约更改
如果枚举的整数值是服务面向外部的合约的一部分,则更改整数可能会破坏客户端。
枚举有两个主要面向外部的地方:
这是使用枚举意外创建重大更改的最简单方法。从这个枚举开始:
客户端对用户类型使用硬编码值
0
、1
和2
。然后,企业希望弃用超级用户
类型。开发人员删除了该枚举元素。现在有多少行为被打破了?
2
,但会收到无效 Type 值的验证错误1
,但会保存一个基本用户1
表示基本用户,并认为它有一个超级用户2
并且永远不会显示基本用户功能如果我们显式分配了会怎样值添加到开头的枚举字段,然后删除
超级用户
?无意外损坏:
3
仍然有效 对于2
将收到无效值的验证错误2
并且永远不会显示超级用户功能HTTP 情况也可以通过将枚举序列化为字符串而不是数字来缓解。但是,如果您确实需要最小化有效负载大小,那么这并不理想。字符串枚举序列化在数据库端不太常见,我认为是因为同一个团队通常拥有数据库和使用它的服务,而 API 客户端可能更加分散,通信可能更具挑战性。
结论2:
Always explicitly assign values to each enum field, to prevent accidental breaking changes. But never assign `0` (except for flag enums), so that you can differentiate between unassigned properties and valid values.
Assign all values starting at 1 and use `Nullable{T}` to represent no value, unless you can't
I appreciate Microsoft's framework guidelines, but I disagree with them on enum practices. I think there is a lot of subtlety in the various use cases of enums that aren't really addressed there, or in other answers here.
However, flag enums really do need a
None = 0
value to work. The rest of this answer does not apply to flag enums.Also, before going on, it might be good to state the golden rule of C# enums:
Enums are a semi-type-safe minefield
For this answer, I will use this hypothetical enum:
There are different ways we might use this enum type.
Case 1: Part of a data structure queried from the DB
Case 2: Part of a search query
Case 3: Part of a POST request
These three classes all look the same, but the data comes from different places and is validated and processed differently.
Case 1: Part of a data structure queried from the DB
We can make some assumptions about the validity of this data, because it should have been validated before saving.
Name
should be a valid non-empty string.Type
should be eitherBasic
orAdmin
, nevernull
or some other invalid value. (For now, ignore how this property is persisted, whether asINT
/VARCHAR
/etc.)Name
orType
. If using newer C# language features, theName
property might be declared as non-nullable (string! Name
), although this might not be directly supported by all ORMs, and so you may need to validate against nulls after querying data.Case 2: Part of a search query
This is a client request, so there may be invalid input. Additionally, these properties should be optional, so clients can search using only the filters they care about.
You might want to model this type using
Nullable<T>
for value types and explicit nullable reference types.Things you may want to validate:
Name
is eithernull
or a non-empty string. Alternately, you may just treat empty or whitespace asnull
. (You probably don't want to validate the value is a real user name. If its not valid, the search will return 0 results.)Type
is a valid enum value, or some representation of "no filter". For example, if a client sendsType = "Superuser"
this may indicate a client bug and a 400 response would be helpful.Case 3: Part of a POST request
This is also client input, but these properties should not allow
null
/blank values, and there will be different validation rules.Things you may want to validate:
Name
is a non-null
, non-empty stringName
is at least X characters longName
does not contain punctuation or whitespaceType
is a valid valueLike case 1, you may want to use
string! Name
to more accurately represent your data. However, if this is being parsed from HTTP requests, you may need to explicitly validate against nulls still, depending on the framework you are using.So, what is the best way to represent "no type"?
The framework guidelines say that we should add an element to our enum to represent this:
So how does this affect our 3 use cases? It affects all of them, because they are all using
UserType
.Case 1: Part of a data structure queried from the DB
Instances of
UserQueryResult
can now be created withType = UserType.None
.Of course, this isn't the first invalid state our typing allows.
UserQueryResult
already allowedName = ""
, but we are adding a possible invalid state.In places where we access
UserQueryResult.Type
, we should already have a defensive way to handle invalidUserType
values, since the type system allows things like(UserType)999
.Case 2: Part of a search query
If we stick with using
Nullable<T>
for value types on our optional properties, we now have two ways to represent "do not filter onUserType
".Type = UserType.None
Type = null
This means anywhere we use this type we need some
&&
or||
logic to deal with both cases.If we get rid of
Nullable<T>
on enum types but leave it on other value types, then we reduce the number of options, but have a more complicated API contract with multiple conventions for us and clients to remember.Case 3: Part of a POST request
The types now allow
Type = UserType.None
on this request. We'll need to add a special validation rule to check against this.What we can see from the effects of this change on these 3 cases is that we have coupled the list of valid values to the representation of "no value". "No value" is only valid for Case 2, but we have forced the code for Case 1 and Case 3 to handle extra "no value" complexity.
Additionally, we can see in Case 2 that we already have a generic way to represent "no value" for value types, which is
Nullable<T>
. In many ways this resembles thenull
handling for reference types, bringing us close to a single unified way to represent "no value" across all types, reducing developer mental load.Conclusion 1
Use
Nullable<T>
for "no value", for consistency, and so that you have a distinct type to represent "a value that is never 'no value'".So that is why you shouldn't add a
None
value. But why should you explicitly assign enumint
values?Reason 1: Unassigned properties have the value
default(T)
For reference types,
default(T) == null
.For value types,
default(T) == (T)0
.Let's say a client wants to POST a request to create a new user. A good JSON payload would look like this:
(For readability, our JSON parser is configured to accept strings for enums. Whether using strings or ints for enums in JSON is not really relevant here.)
As expected, this payload will be parsed to a C# object like this:
What happens if our client sends incomplete JSON?
This will be parsed as
Again,
default(UserType) == (UserType)0
.Our enum could be declared in one of three ways:
None
(None = 0
, or justNone
implicitly assigned to0
)Admin
implicitly assigned to0
Admin = 1
In case 1,
Type
gets parsed asNone
. SinceNone
is part of our enum, we already need to validate against this case to prevent savingNone
to the DB. However, I already covered the reasons why you shouldn't have aNone
value.In case 2,
Type
gets parsed asAdmin
. After that happens, there isn't a way to differentiate between anAdmin
value that came from"Type": "Admin"
in the payload, vsType
missing in the payload. This is obviously not good.In case 3,
Type
gets parsed as(UserType)0
, which doesn't have a name. This looks odd at first, but is actually the best possible scenario. Because enums allow invalid values (like(UserType)999
), we should be validating against invalid values from clients anyway. This just makes "unassigned" an invalid value instead of a valid one.To me, case 3 also seems well aligned with the recent additions to C# that make it harder to represent invalid values: non-nullable reference types and required properties. Conversely, case 1 feels like a legacy pattern from C# 1, before generics and
Nullable<T>
.Reason 2: Avoid accidental contract changes
If your enum's integer values are part of the external-facing contract of your service, changing the integers can break clients.
There are two main places enums are external-facing:
Here is the easiest way to accidentally create a breaking change with enums. Start with this enum:
Clients are using hardcoded values
0
,1
, and2
for user types. Then the business wants to deprecate theSuperuser
type. A dev removes that enum element.How many behaviors are now broken?
2
for a Basic user, but it will get a validation error for an invalid Type value1
for a Superuser, but it will save a Basic user1
for a Basic user and think it has a Superuser2
and will never show Basic user functionalityWhat if we had assigned explicit values to the enum fields at the beginning, and then removed
Superuser
?No accidental breakage:
3
for Basic users still works2
for a Superuser will get a validation error for an invalid value2
and will never show Superuser functionalityThe HTTP case can also be mitigated by serializing enums as strings instead of numbers. However, that isn't ideal if you really need to minimize payload sizes. String enum serialization is less common on the DB side, I think because the same team often owns the DB and the service using it, whereas API clients can be more distributed and communication can be more challenging.
Conclusion 2:
Always explicitly assign values to each enum field, to prevent accidental breaking changes. But never assign `0` (except for flag enums), so that you can differentiate between unassigned properties and valid values.
不要分配任何数字。
就像应该使用的那样使用它。
Don't assign any numbers.
Just use it like it supposed to be used.
如果你从 1 开始,那么你可以很容易地清点你的东西。
如果从 0 开始,则使用第一个作为未初始化事物的值。
If you start at 1, then you can easily get a count of your things.
If you start at 0, then use the first one as a value for uninitialized things.
如果由于以下原因 Enum 没有默认值的概念,则最好将第一个 Enum 成员的值设置为
1
。Intuition
C# 默认将 Enum 设置为
0
。因此,除非第一个 Enum 成员确实是默认值,否则直观上不将其映射到0
。允许强制执行 Web API 所需的枚举
考虑以下最小 Web API:
假设客户端强制为
MyEnum
提供值;发送空 JSON 字符串{}
会导致端点返回false
。然而,上面的实现返回
true
;模型验证通过,因为 C# 默认MyEnum
为0
,它映射到MyEnum.One
。通过将 Enum 修改为
enum MyEnum { One = 1, Two }
,端点返回false
;模型验证失败,因为没有任何 Enum 成员映射到0
。警告
Enum 的指南文档 状态
但违反这一准则似乎并不会导致负面后果。
Prefer setting the first Enum member's value to
1
if the Enum does not have a concept of default value for the following reasons.Intuition
C# sets the Enum to
0
by default. So, unless that first Enum member is really a default value, it's intuitive to not have it mapped to0
.Allows Enforcing of Required Enums for Web API
Consider the following Minimal Web API:
Suppose that it's mandatory for the client to supply a value for
MyEnum
; sending an empty JSON string{}
results in the endpoint returningfalse
.However, the above implementation returns
true
; Model Validation passes because C# defaultsMyEnum
to0
, which is mapped toMyEnum.One
.By modifying the Enum to
enum MyEnum { One = 1, Two }
, the endpoint returnsfalse
; Model Validation fails because none of the Enum's members are mapped to0
.Caveat
Enum's guidelines documentation state
But it doesn't seem that violating this guideline leads to negative consequences.
首先,除非您出于某种原因指定特定值(数值在其他地方具有含义,即数据库或外部服务),否则根本不要指定数值并让它们明确。
其次,您应该始终有一个零值项(在非标志枚举中)。该元素将用作默认值。
First of all, unless you're specifying specific values for a reason (the numeric value has meaning somewhere else, i.e. The Database or external service) then don't specify numeric values at all and let them be explicit.
Second of all, you should always have a zero value item (in non-flags enums). That element will be used as the default value.
不要从 0 开始,除非有原因,例如将它们用作数组或列表的索引,或者有其他实际原因(例如在按位运算中使用它们)。
您的
enum
应该准确地从它需要的地方开始。它也不必是连续的。如果明确设置这些值,则需要反映某些语义或实际考虑。例如,“墙上的瓶子”的枚举
应该从1到99编号,而4的幂的枚举
可能应该从4开始并以16继续、64、256 等。此外,只有在表示有效状态时才应将零值元素添加到枚举中。有时“无”、“未知”、“缺失”等是有效值,但很多时候它们不是。
Don't start them at 0 unless there's a reason to, such as using them as indices to an array or list, or if there's some other practical reason (like using them in bitwise operations).
Your
enum
should start exactly where it needs to. It needn't be sequential, either. The values, if they are explicitly set, need to reflect some semantic meaning or practical consideration. For example, anenum
of "bottles on the wall" should be numbered from 1 to 99, while anenum
for powers of 4 should probably start at 4 and continue with 16, 64, 256, etc.Furthermore, adding a zero-valued element to the
enum
should only be done if it represents a valid state. Sometimes "none," "unknown," "missing," etc. are valid values, but many times they are not.如果枚举以零开头,则无需分配整数值。它以 0 & 开头增加 1。Inactive = 0,Active = 1。
如果要为第一个指定特定值,则需要为其指定值。这里,Inactive = 1,Active = 0。
if the enum starts with zero then no need to assign integer values.It starts with 0 & increment by 1. Inactive = 0, Active = 1.
If you want to assign specific value for 1st one, you need to assign the value for it.Here, Inactive = 1, Active = 0.
我喜欢从 0 开始枚举,因为这是默认值,但我也喜欢包含一个值为 -1 的未知值。然后,这将成为默认值,有时可以帮助调试。
I like to start my enums at 0, since that's the default, but I also like to include a Unknown value, with a value of -1. This then becomes the default and can help with debugging sometimes.
框架设计指南:
框架设计指南/设计标志枚举:
Framework Design Guidelines:
Framework Design Guidelines / Designing Flag Enums:
好吧,我想我不同意大多数不明确编号的答案。我总是对它们进行显式编号,但这是因为在大多数情况下,我最终将它们保存在数据流中,并将它们存储为整数值。如果您不显式添加值,然后添加新值,则可能会破坏序列化,然后无法准确加载旧的持久对象。如果您打算对这些值进行任何类型的持久存储,那么我强烈建议显式设置这些值。
Well, I guess I stand in disagreement with most answers that say not to explicitly number them. I always explicitly number them, but that is because in most cases I end up persisting them in a data stream where they are stored as an integer value. If you don't explicitly add the values and then add a new value you can break the serialization and then not be able to accurately load old persisted objects. If you are going to do any type of persistent store of these values then I would highly recommend explicitly setting the values.
Enum 是一种值类型,如果未显式初始化,其默认值(例如类中的 Enum 字段)将为 0。
因此,您通常希望将 0 作为定义的常量(例如未知)。
在您的示例中,如果您希望
Inactive
成为默认值,那么它的值应该为零。否则,您可能需要考虑添加常量Unknown
。有些人建议您不要显式指定常量的值。在大多数情况下可能是个好建议,但在某些情况下您会想要这样做:
标记枚举
其值用于与外部系统(例如COM)互操作的枚举。< /p>
An Enum is a value type and its default value (for example for an Enum field in a class) will be 0 if not initialized explicitly.
Therefore you generally want to have 0 as an defined constant (e.g. Unknown).
In your example, if you want
Inactive
to be the default, then it should have the value zero. Otherwise you might want to consider adding a constantUnknown
.Some people have recommended that you don't explicitly specify values for your constants. Probably good advice in most cases, but there are some cases when you will want to do so:
Flags enums
Enums whose values are used in interop with external systems (e.g. COM).