1

Тема: За яким правилом C# вибирає який елемент enum буде повернуто ?

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

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

¿ Чому ?

2

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

Я так розумію, це питання внутрішньої оптимізації. З точки зору компілятора None і NO еквівалентні. Просто не треба покладатися на автоматику в складних питаннях - звідки компілятору знати, що вам потрібне перше за положенням у тексті визначення в enum-і, а не найкоротше чи перше за алфавітом? Визначайте власну функцію.

Підозрюю, що, якщо визначати одні члени enum-а через інших, то назви запишуться саме від перших:

Прихований текст
public enum FlipRotate2dEnum : byte {
    None             = 0, 
    RotateTwice        , 
    FlipX              , 
    FlipY              , 
    ReflectDiagonal1   , 
    ReflectDiagonal2   , 
    RotateClockwise    , 
    RotateCounterClockwise , 
    NO = None,
    R2 = RotateTwice,
    FX = FlipX,
    FY = FlipY,
    D1 = ReflectDiagonal1,
    D2 = ReflectDiagonal2,
    RC = RotateClockwise,
    RCC = RotateCounterClockwise
}

https://ideone.com/d8WrwM

Прихований текст

Цікаво навіть, а якщо так? https://ideone.com/YDKz60 Ага, невдача. Оптимізатор надто розумний.

Подякували: leofun011

3

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

koala написав:

З точки зору компілятора None і NO еквівалентні. Просто не треба покладатися на автоматику в складних питаннях - звідки компілятору знати, що вам потрібне перше за положенням у тексті визначення в enum-і, а не найкоротше чи перше за алфавітом? Визначайте власну функцію.

Я не очікую, що компілятор буде знати, що мені потрібно. Просто я хочу розуміти чому компілятор працює саме так, а не інакше. Я пробував запускати цей код багато разів на різних машинах і результат всюди однаковий, тобто ймовірність того, що це рандомна оптимізація, прямує до нуля (у випадку, коли всі елементи enum'а мають присвоєні значення). Значить тут працює який конкретний алгоритм і я хочу дізнатись який саме.

4

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

До речі ті всі значення, які я поприсвоював елементам enum'а повинні бути саме такими як там написано. Їх не можна просто викинути. Це пов'язано з використанням методів, про які я тут не згадав. Про це детальніше буде тема в розділі про алгоритми.

5

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

А де у мене ті значення змінюються?

Подякували: leofun011

6

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

koala написав:

А де у мене ті значення змінюються?

Під споілером був приклад коду

public enum FlipRotate2dEnum : byte {
    None             = 0,
    RotateTwice        ,
    FlipX              ,
    FlipY              ,
    ReflectDiagonal1   ,
    ...

І значення там такі як треба. До вас претензій немаю.
Просто дехто може подумати, що рядки можна переставляти місцями і при цьому залишати без присвоєнь. А це не так.

Подякували: koala1

7

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

І дійсно, ані в стандарті, ані в описі форматування про те, який зі синонімів обирається для перевторення, не сказано. Мабуть, виходять з того, що якщо є синоніми - то не має бути значення, який з них брати.

Подякували: leofun011

8

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

А ні, знайшов: https://msdn.microsoft.com/en-us/librar … .110).aspx

Notes to Callers:
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. For example, the following enumeration defines two members, Shade.Gray and Shade.Grey, that have the same underlying value.

Компілятор сам визначає, який зі синонімів брати. Робити будь-які припущення некоректно.

Подякували: leofun01, FakiNyan, DOP3

9

Re: За яким правилом C# вибирає який елемент enum буде повернуто ?

На stackoverflow мені пояснили як це працює.
Є масив значень типу byte
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7
і є масив імен типу string
NO, None, R2, RotateTwice, FX, FlipX, FY, FlipY, D1, ReflectDiagonal1, D2, ReflectDiagonal2, RC, RotateClockwise, RN, RotateNonClockwise
Пари (byte, string) зберігаються відсортованими за зростанням значення (byte).
Далі виконується бінарний пошук значення (byte) і повертається відповідна стрічка.
Виглядає це так :

0011223344556677 - масив значень, максимальний індекс 15.
       1      |  - 1-ий крок повертає індекс 7, його стрічка "FlipY".
   1       1  |  - 2-ий крок повертає індекс 3 або 11, їх стрічки "RotateTwice", "ReflectDiagonal2".
 1   1   1   1|  - 3-ий крок повертає індекс 1, 5, 9 або 13, їх стрічки "None", "FlipX", "ReflectDiagonal1", "RotateClockwise".
              1  - 4-ий крок повертає індекс 14, його стрічка "RN".
Подякували: koala, LoganRoss, ReAl, DOP4