为什么默认枚举值是0而不是最小值?

19

在 C# 中,将'0'作为枚举的默认值有什么意义呢?如果我声明一个以不同数字开头的枚举:

enum Color
{
   Blue = 1,
   Green,
   Red,
}

那么 var color = default(Color) - 会返回 '0'。我希望看到的是最小值。如果枚举的每个成员都对应着某个特定的数字,而 '0' 不是一个有效值,这种行为可能会导致问题。


2
为什么要有“最小值”?为什么不能有倒数的枚举? - BoltClock
2
为什么你会将值从0以外的数字开始呢?大多数程序员习惯于使用0作为计数的起点,例如在处理数组时。我建议除非你有一个非常好的理由,否则不要给你的枚举类型赋值。 - Jesse Webb
@JesseWebb 这是一个例子。但我可以声明枚举,其中每个成员都持有某些特定的值。最近我处理了一些特定格式的文件,其中每个条目由1、2或3个标记值定义。 - username
@JesseWebb 有时候,与外部进程的兼容性使得将外部进程的明确数值映射到 .net 应用程序内的易读句柄更容易。如果您处理返回 8 到 27 之间的值的旧系统(因为 0..7 已经在 8 年前被弃用),则可以更容易地将这些值数字化为枚举值。 (虽然是的,在一般情况下不应该发生这种情况。) - Cornelius
8个回答

14

所有值类型(包括枚举)的默认值都是按位0。因此,这意味着即使未明确定义,0也始终是枚举的可能值。

以下是规范:默认值表

编辑:有关详细信息,请查看MSDN中的枚举类型 - 枚举

枚举是一种特殊的类型,可以从其他值类型“派生”出来(通常不可能),但在其后备类型的值类型中表现出行为。枚举可以仅使用某些整数值作为后备类型。

注意:正如@archil指出的那样,枚举值可以包含后备类型的任何值,而与枚举本身枚举的常量列表无关。对于标记有“FlagsAttribute”的枚举,通常期望这种行为,但对于普通枚举来说可能并不直观。


1
我已经阅读了文档,谢谢,但问题是为什么这样。枚举不应该只包含在代码中声明的值吗?现在我必须创建默认的0元素并始终检查它是否不是该元素。而且我不能安全地期望枚举成员总是我提供的那个。我想这种方式在C#中很容易实现。 - username
1
不是的。枚举可以包含任何值作为其支持类型(默认为int)。出于同样的原因,它具有支持类型的默认值,除非另有规定。使用Enum.IsDefined(value)函数验证输入的枚举参数。 - Dmitry S.
1
哎呀,我在发帖之前应该先看看这个答案。不过这个答案有点太笼统了:枚举类型并不是值类型,但它们的后备类型是值类型。枚举类型不能使用任何值作为其后备类型,只能使用整数值。值得澄清这些观点。 :) - Dan J
在我看来,这违背了枚举的“用例”——将类型限制为您在列表中定义的内容。叹气。“按设计要求工作”。 - ashes999
@TimSchmelter "但是"? DateTime.MinValue 是按位 0 - 不确定其中一个如何与另一个相反。 - Alexei Levenkov
显示剩余3条评论

10

我们只能猜测.NET框架某个方面为什么会以特定的方式设计。为了提供最简洁的解释,我想强调一下MSDN文档中的这句话:

枚举是一组命名常量,其底层类型可以是任何整型,除了Char。如果没有显式声明底层类型,则使用Int32。

请注意,.NET枚举本质上是整型类型的扩展。 整型类型的默认值为0,因此合理地(尽管在您所示例的情况下有点不太方便)让枚举继承该行为。


但是,当枚举不使用0值时,我遇到了“System.NullReferenceException类型的异常在MyProjectName.dll中发生,但未在用户代码中处理。其他信息:对象引用未设置为对象的实例。”错误。如何解决?注意:我使用扩展方法来显示枚举的描述,但不确定如何避免在此页面上定义的帮助程序方法(GetDescription)中出现错误。 - Jack
1
@Christof,如果没有看到你的代码,我无法回答这个问题,但是你所描述的异常表明一个变量意外地被设置为null。这就是在调试时要查找的内容。 - Dan J
好的,谢谢。我通过在Enum ToString with user friendly strings上使用GetDescription()方法解决了这个问题。 - Jack

7

枚举类型有可能存在不存在的值,因此存在Enum.IsDefined方法

枚举类型E的默认值是表达式(E)0所产生的值。


@BoltClock 但是根据我的检查,我编辑了我的答案以提供更正确的定义。 - archil
你是在说如果枚举没有在代码中声明,它可以有除 0 以外的其他值吗? - username

3

到目前为止,已经有多个答案表明,任何固定大小数据结构的“默认”值都是0。该结构内存的大小设置为零,这被认为是“默认值”。现在,如果您想为枚举定义自己的“默认”值,则有多种选项。最简单的方式是编写自己的函数以为您提供“最小有效枚举”。

private E GetDefaultEnum<E>()
{
    return (E)(Enum.GetValues(typeof(E)).Cast<E>().Min<E>());
}

使用方法:

enum Color
{
   Blue = 1,
   Green,
   Red,
}

Color defaultColor = GetDefaultEnum<Color>();
MessageBox.Show(defaultColor.ToString()); // "Blue"

你可以明显地修改你如何确定“默认”值,但是这种方法似乎最适合你的需求。

1
为什么枚举类型的默认值是0?因为这样可以方便地在运行时实现。任何值类型的默认值都是按位0,一个空引用也被看作按位0表示。因此,要初始化一个带有其默认值的对象,我们只需要知道其大小;运行时不需要了解或处理对象的结构。同样地,预定义的值类型构造函数只需用零填充内存区域即可。
无论你喜不喜欢,这就是事实。在设计枚举类型时,无论名称如何,都应该将其默认值设置为0。如果没有合适的默认值,则接受此类枚举类型参数的方法可能会检查它是否已正确设置。
void ProcessColor(Color c) {
    if (c == 0)
        throw new ArgumentException("Color is not set", "c");
    //...
}

是的,这就是我现在要做的,检查它是否不为0。但无论如何,在枚举中包含默认值0以避免任何隐式不一致性总是更好的选择。谢谢。 - username

0

enum 默认是 int 类型。Enums 是我们程序源代码中可读性较好的整数类型,但当你的程序被编译后,不存在 enum 的概念,只有 int。整数类型的默认值为 0。

以下是代码:

public enum test { One, Two, Three }

var x = test.One;
var y = 0;

在IL中变成了这样:

IL_0001:  ldc.i4.0   
IL_0002:  stloc.0     
IL_0003:  ldc.i4.0    
IL_0004:  stloc.1 

那么,在某些情况下,一个 int 怎么可能有默认值为1(正如你所问的),而在其他情况下则没有呢?

0

关键在于可能是拥有更少的正整数可能值。

为什么要更少的正整数?

为了能够正确地处理具有位移操作的枚举。

我自然而然地谈论默认值,这可以由开发人员更改。


0

我不确定为什么但是...

这里有关于C#枚举的文档:http://msdn.microsoft.com/en-us/library/sbbt4032.aspx

枚举器可以使用初始化程序来覆盖默认值,如下例所示。

enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

在此枚举中,元素序列被强制从1开始而不是0。但是,建议包含一个具有0值的常量。有关更多信息,请参见枚举类型(C#编程指南)

他们建议您始终包括一个占位符枚举值,该值对应于值0,否则可能会遇到问题。您可以将此枚举值命名为未定义(Undefined)默认(Default)等名称。

正如我在评论中提到的,我的建议是完全不要为您的枚举使用值,除非您有非常好的理由这样做。如果需要,请遵循MSDN枚举类型编程指南


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接