无法理解枚举的行为

本文关键字:枚举 | 更新日期: 2023-09-27 18:21:05

我在C#中练习枚举,无法理解这些的输出

void Main()
{
    MyEnum a = MyEnum.Top;
    Console.WriteLine(a);
}

对于这个测试,我的枚举和输出是

enum MyEnum
{
    Left, Right, Top, Bottom // Top
}
enum MyEnum
{
    Left, Right, Top = 0, Bottom // Left
}

我认为在朗姆酒时间程序选择值为0的第一个项目,以确认这一点,我将值0分配给底部

enum MyEnum
{
    Left, Right, Top = 0, Bottom = 0 // Bottom
}

然后我想也许程序选择了值为0的第一个项目,但按字母顺序搜索所以我将Top更改为ATop,并更改了测试用例

void Main()
{
    MyEnum a = MyEnum.ATop;
    Console.WriteLine(a);
}
enum MyEnum
{
    Left, Right, ATop = 0, Bottom = 0 // Bottom
}
enum MyEnum
{
    Left, Right, ATop = 0, Bottom // Left
}

我知道没有人以这种方式使用枚举,但我想知道枚举的这种特殊行为。

无法理解枚举的行为

似乎您已经发现编译器默认情况下开始计数为零。

enum MyEnum
{
    Left, Right, Top = 0, Bottom = 0 // Bottom
}

被翻译为此

.class nested private auto ansi sealed MyEnum
    extends [mscorlib]System.Enum
{
    .field public specialname rtspecialname int32 value__
    .field public static literal valuetype X/MyEnum Left = int32(0)
    .field public static literal valuetype X/MyEnum Right = int32(1)
    .field public static literal valuetype X/MyEnum Top = int32(0)
    .field public static literal valuetype X/MyEnum Bottom = int32(0)
}

运行时实际上大部分时间都与底层类型一起工作。有趣的是这个

static void Main()
{
    MyEnum a = MyEnum.Top;
    Console.WriteLine(a);
    Console.ReadKey();
}

甚至不使用实际的枚举成员:

.method private hidebysig static 
    void Main () cil managed 
{
    .maxstack 1
    .entrypoint
    .locals init (
        [0] valuetype X/MyEnum a
    )
    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: box X/MyEnum
    IL_0009: call void [mscorlib]System.Console::WriteLine(object)
    IL_000e: nop
    IL_000f: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
    IL_0014: pop
    IL_0015: ret
}

它只使用值0。决定用Console.WriteLine打印出哪个名称从System.Enum.ToString开始,并在System.Type.GetEnumName(object)达到高潮。

public virtual string GetEnumName(object value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (!this.IsEnum)
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
    }
    Type type = value.GetType();
    if (!type.IsEnum && !Type.IsIntegerType(type))
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
    }
    Array enumRawConstantValues = this.GetEnumRawConstantValues();
    int num = Type.BinarySearch(enumRawConstantValues, value);
    if (num >= 0)
    {
        string[] enumNames = this.GetEnumNames();
        return enumNames[num];
    }
    return null;
}

正如您所看到的,搜索要打印的名称的实际方法是通过字段名称进行二进制搜索(通过反射找到)。

不过,这只是当前的实现,不同的编译器和/或运行时版本可能会有所不同。语言规范并不能保证像上面这样的代码有任何特定的顺序或结果。

Enum不过是命名一些常量。

就像我们有一些水果。苹果、香蕉、桔子。我们有一个场景,如果一个婴儿1岁,它会得到橙色,如果2岁会得到香蕉,如果3岁会得到苹果。那么我们是怎么写的呢?

一种解决方案是:

    int fruiteType = 0;
    if(ageOfBaby == 1)
        fruiteType == 1;//assuming Orange = 1; 
    if(ageOfBaby == 2)
        fruiteType == 2;//assuming Banana = 2; 
    if(ageOfBaby == 3)
        fruiteType == 3;//assuming Apple = 3; 

我们可以巧妙地使用Enum。类似:

    enum Fruits {Orange, Banana, Apple};
    int fruiteType = 0;
    if(ageOfBaby == 1)
        fruiteType == (int)Fruits.Orange;
    if(ageOfBaby == 2)
        fruiteType == (int)Fruits.Banana;
    if(ageOfBaby == 3)
        fruiteType == (int)Fruits.Apple;

默认情况下,在上述条件下,您将分别获得fruiteType变量中的0、1、2。如果你想更改默认值,你可以定义如下:

  enum Fruits {Orange = 1, Banana, Apple};

为什么这很重要。当我们假设这样的值时,我们可能会忘记序列,从而造成麻烦。但当我们命名它们时,我们永远不会忘记哪个值代表fruitType=1。

感谢