Правильное название
Название — один из важных моментов в разработке. Шутки в сторону. Лучшее имя для перечисления должно отражать, из чего оно состоит. Хорошие имена короткие и говорят сами за себя: WindowName, EntityType и т. д. Это так просто. Нет причин добавлять «s» или «enum» или называть его ListOfEntitiesType. Краткость — душа остроумия. И не забудьте использовать пространство имен, чтобы ваши перечисления были аккуратно разделены ими.
Какими могут быть значения членов перечисления?
По умолчанию это int, и я рекомендую придерживаться его. Но иногда требуется сделать что-то другое, и для значения членов перечисления вы можете использовать любой целочисленный числовой тип, который вам нравится: байты, целые, длинные и даже короткие.
Правильная индексация
Многие разработчики используют недопустимые индексы по умолчанию. Например -1 или int.MinValue, или -99999. Эти вещи раздражают. Давайте посмотрим на пример: представьте, что у вас есть перечисление DamageType и класс Damage, который вы куда-то отправляете, чтобы нанести урон.
enum DamageType
{
None = -1,
Range = 0,
Magic = 1,
Close = 2,
}
class Damage
{
public DamageType Type { get; private set; }
public int Amount { get; private set; }
…..
}
Если вы создадите этот класс с пустым конструктором, вы получите следующие значения по умолчанию: Type — DamageType.Range, а Amount — 0. Что нелогично, поскольку по умолчанию для DamageType будет None. Если ваша цель не другая.
Полезные фрагменты
Как перебрать все элементы перечисления? Легкий:
var allElementsOfEnum = Enum.GetValues(typeof(MyEnum));
С этого момента мы можем легко получить случайные элементы перечисления. Или создайте по одной сущности каждого типа.
И это немного сложно, но в то же время интересно. Представьте, что вы работаете над реальным симулятором животных. У вас есть еж, который живет на земле и едва умеет плавать, у вас есть окунь, который живет в воде и едва может жить на поверхности:
public enum AnimalHabitat {
None = 0,
Ground = 1,
Water = 2,
}
…и тут приходит геймдизайнер и просит добавить черепаху, которая живет и на земле, и под водой. Мы могли бы добавить в перечисление Amphibian еще один элемент, равный 3. И тогда нам нужно будет проверить среду обитания животных следующим образом:
If (animal.habitat == AnimalHabitat.Ground ||
animal.habitat == AnimalHabitat.Amphibian)
{
//do the ground stuff
}
...
if (animal.habitat == AnimalHabitat.Water ||
animal.habitat == AnimalHabitat.Amphibian)
{
//do the water stuff
}
И затем каждый раз, когда мы будем добавлять пересекающееся поведение, нам нужно будет снова модифицировать наш код. И проблема в том, что это мешает принципу SOLID. Если быть точным, кнопка S означает, что должна быть только одна причина для изменения класса. И я не вижу смысла менять здесь поведение наземной или водной среды обитания. Нам просто нужно проверить, есть ли у Амфибии свойства поведения на земле или в воде.
Итак, давайте попробуем другой способ!
[Flags]
public enum AnimalHabitat
{
None = 0b_0000_0000,
Ground = 0b_0000_0001,
Water = 0b_0000_0010,
Amphibian = Ground | Water, //that will be 0b_0000_0011
}
И тогда вам нужно будет вспомнить немного магии. Магия побитовых операций.
var habitat = AnimalHabitat.Amphibian;
if (habitat & AnimalHabitat.Ground == AnimalHabitat.Ground) { //do the ground stuff } ... if (habitat & AnimalHabitat.Water == AnimalHabitat.Water) { //do the water stuff }
Имейте в виду, что в этой ситуации у вас может быть 8 флагов (включая ноль, который будет означать «Нет», поскольку мы заключили сделку ранее). И это даст вам 256 комбинаций.
Заключение
Как мы видим, Enums может быть мощной структурой данных, которая может помочь построить логику игры, над которой вы работаете. И правильное использование перечислений не только принесет пользу в процессе разработки, но и поможет поддерживать игру в будущем.