Как преобразовать значение примитивного типа в значение enum, если enum содержит элементы с одинаковыми значениями? - PullRequest
0 голосов
/ 11 мая 2018

Я написал код:

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);
        }
    }
}

и я ожидаю увидеть в output:

только короткие имена

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

¿Почему? Пожалуйста, объясните мне, что здесь происходит.

1 Ответ

0 голосов
/ 11 мая 2018

Переменная Enum - это просто число, к ней не прикреплена метка (например, R2). Например, FlipRotate2dEnum.RN и FlipRotate2dEnum.RotateNonClockwise - это одно и то же, потому что они имеют одинаковое значение 7. На самом деле, если вы делаете:

Console.WriteLine(FlipRotate2dEnum.RotateNonClockwise);

Вы увидите RN как вывод. Как указано в документации Enum.ToString()

Если несколько членов перечисления имеют одинаковое базовое значение, и вы попытка получить строковое представление перечисления имя участника, основанное на его базовом значении, ваш код не должен делать любые предположения о том, какое имя метод возвратит

Но если вам интересно, как именно результат, который вы видите, получается изнутри, читайте ниже.

Во-первых, массив базовых значений (в вашем случае - байтов) получается примерно примерно так (но не совсем так):

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

Он не содержит различных значений, только все значения в enum отсортированы. Так что в этом случае 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];

Теперь, если вы знаете, как работает бинарный поиск - теперь вы должны понять, почему результаты не совсем то, что вы ожидаете. Поскольку двоичные значения поиска не изучаются последовательно в том виде, в котором они определены, вместо этого поиск начинается с середины и сокращает пространство поиска наполовину при каждой итерации, поэтому не очень очевидно, какому имени сопоставляется ваше значение, когда он играет главную роль при объявлении перечисления.

Конечно, вы можете достичь желаемого результата, просто поменяв местами RN и RotateNonClockwise, но я надеюсь, что вы понимаете из вышесказанного, что это не очень хорошая идея, не говоря уже о том, что вы полагаетесь на детали реализации и addind \ удаление новых участников будет проблемой.

Как указано в комментариях - если вам нужно «отображать» имена для ваших перечислений - украсьте их некоторым атрибутом и используйте его. Старайтесь избегать перечислений с несколькими именами, сопоставляющими одно и то же значение, но если вы это сделаете - по крайней мере, не думайте об этом как о взаимно-однозначном сопоставлении значения и имени, потому что это не так.

...