В Go есть идиоматический способ объявления перечислений, который многие (включая меня) хвалят за его простоту:
type Day int const ( Monday Day = 1 + iota Tuesday Wednesday Thursday Friday Saturday Sunday )
Другие языки обычно имеют больше возможностей для типов enum, например в Java:
public enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }
И тогда вы можете использовать такие функции, как:
for (Day day: Day.values()) { if (day == Day.Monday) { System.out.printf("Monday is day number %d\n", day.ordinal()); } System.out.printf("Day of the week: %s\n", day); }
В C# вы можете воспользоваться аналогичными проверками безопасности типов и немного другим синтаксисом для базового типа:
enum Days : byte {Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
Давайте теперь еще раз взглянем на то же перечисление в Go, сосредоточив внимание на том, что мы могли бы использовать:
type Day int // a String() method for each value, so that we can printf it during development/logging? const ( Monday Day = 1 + iota // more complex types maybe? Tuesday Wednesday Thursday Friday Saturday Sunday // boundary check so that I cannot inadvertently create an 8th day of the week? ) // array/slice of allowed values?
Обсуждая с другими программистами Go, я пришел к идее использовать несколько более строгий синтаксис для перечислений Go, (ab)используя переключатели типов и чудеса шаблонизации (с помощью go:generate), результатом чего является go- роды.
Сгенерированный код для этого примера будней с go-genums (некоторые части отредактированы для простоты):
package main // *** generated with go-genums *** // DayEnum is the the enum interface that can be used type DayEnum interface { String() string Value() dayEnum uniqueDayMethod() } // dayEnumBase is the internal, non-exported type type dayEnumBase struct{ value dayEnum } // Value() returns the enum value func (eb dayEnumBase) Value() dayEnum { return eb.value } // String() returns the enum name as you use it in Go code, // needs to be overriden by inheriting types func (eb dayEnumBase) String() string { return "" } // ... [redacted declaration of a type+methods for each allowed enum value] var internalDayEnumValues = []DayEnum{ Sunday{}.New(), Monday{}.New(), Tuesday{}.New(), Wednesday{}.New(), Thursday{}.New(), Friday{}.New(), Saturday{}.New(), } // DayEnumValues will return a slice of all allowed enum value types func DayEnumValues() []DayEnum { return internalDayEnumValues[:] } // NewDayFromValue will generate a valid enum from a value, or return nil in case of invalid value func NewDayFromValue(v dayEnum) (result DayEnum) { switch v { case daySunday: result = Sunday{}.New() case dayMonday: result = Monday{}.New() case dayTuesday: result = Tuesday{}.New() case dayWednesday: result = Wednesday{}.New() case dayThursday: result = Thursday{}.New() case dayFriday: result = Friday{}.New() case daySaturday: result = Saturday{}.New() } return } // ... [redacted]
Результирующий сгенерированный код перечисления, тем не менее, длиннее и сложнее, но вам не нужно вводить его, так как он генерируется автоматически; среди функций, добавленных на этом этапе генерации кода, лично мне больше всего нравится возможность использовать переключатели типов для оценки перечисления:
day := Monday{}.New() switch day.(type) { case Monday: fmt.Println("It's", day) default: panic("It's not Monday!") }
Обратите внимание, что переключение типа значения интерфейса по-прежнему довольно эффективно, так как оно не будет разыменовывать фактическое значение (будь то целое число или большая структура), а вместо этого будет использовать внутренний идентификатор типа Go.
И последнее, но не менее важное: с помощью go-genums вы можете использовать объявление структур для вашего перечисления и автоматически сгенерированные проверяющие фабричные методы, и вы не теряете возможности прямого сравнения переменных перечисления; вы можете прочитать больше и поиграть с ним на странице проекта github.