Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Можно ли использовать Enum без необходимости постоянно кастовать?

#c_sharp


Есть у меня перечисления. Очень большой список. Например:

enum State : byte { 
    IDLE,                       //0 - стойка
    WALK_F,                     //1 - ходьба вперёд
    WALK_B,                     //2 - ходьба назад
    BLOCK,                      //3 - блок
    SIT,                        //4 - сидение
    JUMP_UP,                    //5 - прыжок вверх
    JUMP_F,                     //6 - прыжок вперёд
    JUMP_B,                     //7 - прыжок назад
    BLOCK_SIT,                  //8 - блок в присядке
    TURN,                       //9 - поворот
    RUN,                        //10 - бег.
    ... и т.д.
};


Можно ли как-то применять эти значения в виде, представленным ниже?

byte test = State.RUN;


Или мне постоянно придется писать приведение к типу? Типа:

byte test = (byte)State.RUN;


Вроде бы если enum является перечислением из одного типа, то можно написать 

enum ENUM_NAME : TYPE { ... }; 

и должно все работать. Но IDE упорно говорит что все равно надо кастовать. Может
я что-то не понимаю.  

Просто делать такое приведение типа абсолютно везде чересчур затратно. особенно если
я решу тип сменить. Таких мест будет сотня. Тем более, если я заранее говорю, что у
меня в перечислении будет всё одного конкретного типа, то почему бы такому не работать?
    


Ответы

Ответ 1



Язык вам тонко намекает, что кастовать туда-сюда между enum и целым типом — не самая здравая и строго типизированная мысль. Вам не должно хотеться повсеместно использовать тип byte, если у вас есть enum с базовым типом byte. Просто используйте сам enum везде, где можно. Где нельзя — преобразуйте тип. И этой операции положено быть явной, потому что вы меняете тип переменной. При этом какой у перечисления базовый тип — значения не имеет. Хоть enum Foo : int преобразовывать к int, хоть enum Bar : byte преобразовывать в byte — операция должна быть явной.

Ответ 2



Поскольку в комментариях вы поинтересовались про использование перечисления в качестве индекса массива, отвечу. Допустим, у нас есть перечисление TEnum, основанное на типе int (для простоты) Тогда можно сделать вот так: struct TEnumDict { private readonly T[] values; TEnumDict() { values = new T[...]; } public T this[TEnum index] { get { return values[(int)index]; } set { values[(int)index] = value; } } } Такую структуру можно использовать вместо массива, и она прекрасно индексируется перечислением. Использование такой структуры практически не несет накладных расходов, поскольку все операции над ней очень простые и JIT их заинлайнит. Если же подобных перечислений становится много - то можно добавить уровень обобщения, ценой некоторого замедления... struct IntValuedEnumDict { private static readonly Func valueof; static IntValuedEnumDict() { var e = Expression.Parameter(); valueof = Expression.Lambda>(Expression.Convert(e, typeof(int)), p).Compile(); } private readonly T[] values; IntValuedEnumDict() { values = new T[...]; } // Число элементов надо тоже определить в статическом конструкторе - но мне лень public T this[TEnum index] { get { return values[valueof(index)]; } set { values[valueof(index)] = value; } } } На самом деле, я не уверен, что такая конструкция будет работать быстро. Надо проверять. Или можно не городить велосипеды, а воспользоваться классом Dictionary. Это не массив, а словарь, и работает он несколько медленнее - зато он готовый и универсальный. Но задумайтесь вот над чем. Зачем вам вообще такие перечисления или константы? Учитесь мыслить объектно-ориентированно! Возможная атака - это тоже объект. Почему бы не сделать вот так: class Attack { public int timesPressed; public int atkTimer; public bool atk; } Такой объект можно привязать напрямую к кнопке, избавившись тем самым не только от константы ATTACK_TYPE_PUNCH - но и от строкового литерала "Punch"! Нужно выполнить действие над этими полями? Нет проблем: class Attack { public int timesPressed; public int atkTimer; public bool atk; public void ButtonPressed() { timesPressed = 0; atkTimer = 0; atk = true; } } Для разных типов атак нужен разный код? Нет проблем: abstract class Attack { public abstract void ButtonPressed(); public abstract int AtkTimer { get; } } class PunchAttack : Attack { public int timesPressed; public int atkTimer; public bool atk; public override void ButtonPressed() { timesPressed = 0; atkTimer = 0; atk = true; } public override int AtkTimer { get { return atkTimer; } } }

Комментариев нет:

Отправить комментарий