首页 > 解决方案 > 当枚举包含具有相同值的元素时,如何将原始类型值转换为枚举值?

问题描述

我写了代码:

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

或按字母顺序排序后首先出现的名称,在这种情况下与“仅短名称”一致。

但我没想到会看到程序显示了什么:

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

输出末尾的短名称。 为什么 ?


我试图重新排列 enum 中的列:

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

为什么 ? 请向我解释这里发生了什么。

标签: c#.netenumstype-conversionexplicit-conversion

解决方案


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

Console.WriteLine(FlipRotate2dEnum.RotateNonClockwise);

您将看到RN作为输出。如文档中所述Enum.ToString()

如果多个枚举成员具有相同的基础值,并且您尝试根据其基础值检索枚举成员名称的字符串表示形式,则您的代码不应对该方法将返回哪个名称做出任何假设

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

首先,基本值数组(在你的情况下 - 字节)大致像这样(但不完全像这样)获得:

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

它不包含不同的值,仅包含已排序的枚举中的所有值。所以在这种情况下 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];

现在,如果你知道二分搜索是如何工作的——你现在应该明白为什么结果不是你所期望的。使用二分搜索时,值不会按照定义的方式顺序探索,而是从中间开始搜索,每次迭代都会将搜索空间减少一半,因此在枚举声明时,您的值映射到哪个名称并不是很明显。

当然你可以通过交换RNand来达到你想要的结果RotateNonClockwise,但是我希望你从上面明白这不是一个好主意,更不用说你会依赖于实现细节,添加\删除新成员会很痛苦。

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


推荐阅读