当枚举包含具有相同值的元素时,如何将基本类型值转换为枚举值?

How to convert primitive type value to enum value, when enum contains elements with the same values?

我写了代码:

enum FlipRotate2dEnum : byte {
    NO = 0,    None               = 0,
    R2 = 1,    RotateTwice        = 1,
    FX = 2,    FlipX              = 2,
    FY = 3,    FlipY              = 3,
    D1 = 4,    ReflectDiagonal1   = 4,
    D2 = 5,    ReflectDiagonal2   = 5,
    RC = 6,    RotateClockwise    = 6,
    RN = 7,    RotateNonClockwise = 7
}
class EnumTest {
    public static void Main() {
        for(byte i = 0; i < 8; ++i) {
            FlipRotate2dEnum v = (FlipRotate2dEnum)i;
            System.Console.WriteLine("{0} {1}", i, v);
        }
    }
}

我希望在输出中看到:

只有简称

0 NO
1 R2
2 FX
3 FY
4 D1
5 D2
6 RC
7 RN

或只有长名称

0 None
1 RotateTwice
2 FlipX
3 FlipY
4 ReflectDiagonal1
5 ReflectDiagonal2
6 RotateClockwise
7 RotateNonClockwise

或按字母顺序排序后最先出现的名称,在本例中与 "only short names".

重合

但我没想到会看到程序显示的内容:

0 None
1 RotateTwice
2 FlipX
3 FlipY
4 ReflectDiagonal1
5 ReflectDiagonal2
6 RotateClockwise
7 RN

输出末尾的简称。为什么?


我尝试重新排列枚举中的列:

public enum FlipRotate2dEnum : byte {
    None               = 0, NO = 0,
    RotateTwice        = 1, R2 = 1,
    FlipX              = 2, FX = 2,
    FlipY              = 3, FY = 3,
    ReflectDiagonal1   = 4, D1 = 4,
    ReflectDiagonal2   = 5, D2 = 5,
    RotateClockwise    = 6, RC = 6,
    RotateNonClockwise = 7, RN = 7
}
class EnumTest {
    public static void Main() {
        for(byte i = 0; i < 8; ++i) {
            FlipRotate2dEnum v = (FlipRotate2dEnum)i;
            System.Console.WriteLine("{0} {1}", i, v);
        }
    }
}

我再次对输出感到惊讶:

0 NO
1 R2
2 FX
3 FY
4 D1
5 D2
6 RC
7 RotateNonClockwise

¿ 为什么? 请给我解释一下这是怎么回事。

枚举变量基本上只是一个数字,它没有附加标签(例如R2)。例如,FlipRotate2dEnum.RNFlipRotate2dEnum.RotateNonClockwise 是相同的东西,因为它们具有相同的值 7。实际上,如果您这样做:

Console.WriteLine(FlipRotate2dEnum.RotateNonClockwise);

您将看到 RN 作为输出。如 Enum.ToString()

的文档所述

If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return

但如果您好奇您看到的结果是如何在内部获得的,请阅读下文。

首先,大致像这样(但不完全像这样)获得基础值数组(在您的例子中是字节):

byte[] values = Enum.GetValues(typeof(FlipRotate2dEnum)).Cast<byte>().ToArray();

它不包含 distinct 值,仅包含枚举中的所有值,已排序。所以在这种情况下有 16 个值:

0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7

然后获得名称数组(同样,不完全像这样,但以类似的方式)。该数组也按相应的值排序(即 - 值 0 的名称在值 1 的名称之前等):

var names = Enum.GetNames(typeof(FlipRotate2dEnum));

还包含16个名字:

NO, None, R2, RotateTwice, ...

然后,二进制搜索 对给定值的值数组执行。假设我们调用 FlipRotate2dEnum.RotateNonClockwise.ToString()。它的值为 7,因此对 7 执行二分查找:

var index = Array.BinarySearch(values, (byte)7)

然后,结果索引用于获取名称:

var name = names[index];

现在,如果您知道二分查找的工作原理 - 您现在应该明白为什么结果与您期望的不完全一样。使用二进制搜索,值不会按定义的方式顺序探索,而是搜索从中间开始,每次迭代都会将搜索 space 减少一半,因此在加注星标时将值映射到哪个名称不是很明显在枚举声明中。

当然你可以通过交换 RNRotateNonClockwise 来达到你想要的结果,但我希望你从上面明白这不是一个好主意,更何况你会依赖实现细节,addind\removing 新成员会很痛苦。

如评论中所述 - 如果您需要 "display" 枚举名称 - 用一些属性装饰它们并使用它。尽量避免具有多个名称的枚举映射到相同的值,但如果这样做 - 至少不要将其视为值和名称之间的一对一映射,因为它不是这样工作的。