【问题标题】:How to convert primitive type value to enum value, when enum contains elements with the same values?当枚举包含具有相同值的元素时,如何将原始类型值转换为枚举值?
【发布时间】:2018-10-21 09:21:56
【问题描述】:

我写了代码:

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

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


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

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

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

【问题讨论】:

  • 来自documentation:如果多个枚举成员具有相同的基础值,并且您尝试根据其基础值检索枚举成员名称的字符串表示形式,您的代码不应使关于该方法将返回哪个名称的任何假设。
  • 最好使用[Description] 属性并创建一个方法来检索给定枚举值中包含的值。
  • 正如 john 所建议的 [Description] 将是这里最合适的解决方案,但无论如何您仍然无法使用它。您可以使用const 偏移量来区分两个值。喜欢None = 0, NO = ConstCarrier.Offset + 0,

标签: c# .net enums type-conversion explicit-conversion


【解决方案1】:

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

Console.WriteLine(FlipRotate2dEnum.RotateNonClockwise);

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

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

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

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

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];

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

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

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

【讨论】:

  • 所以,二进制搜索(在这种情况下)看起来像这样:0011223344556677 是字节值,0000000100000000 - 第一步得到第二个“3”,0001000000010000 - 第二步得到第二个“1”或“5”, 0100010001000100 - 第三步得到第二个“0”,“2”,“4”或“6”,0000000000000010 - 第四步得到第一个“7”,这就是为什么我看到姓氏不同?
  • @leofun01 中间元素通常计算为(low + high) / 2,其中low 是搜索范围的低索引(最初为0),high 是高索引(最初长度为-1,因此为15)。所以第一步确实使用了第二个“3”。它比目标(7)小,所以左半边被扔掉了。现在低 = 8,高 = 15。中点 11,即第二个“5”。依此类推,最终到达第一个“7”,即结果。
  • @leofun01 我没有明确提到这一点,但是为了使二分搜索起作用 - 应该对数组进行排序(并且对它们进行排序)。因此,如果您看到中点低于\高于目标 - 可以丢弃一半。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 1970-01-01
  • 2020-12-14
相关资源
最近更新 更多