Перечисления

Перечисления (далее enum) отличаются от классов и определяются с помощью определенного набора конструкторов. Вот небольшой пример:

    enum Color {
        Red;
        Green;
        Blue;
    }

    class Colors {
        static function toInt( c : Color ) : Int {
            return switch( c ) {
                case Red: 0xFF0000;
                case Green: 0x00FF00;
                case Blue: 0x0000FF;

            }
        }
    }

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

Параметры конструктора

Предыдущий пример с Color показывает 3 константных конструктора для enum. Однако, так же возможно передавать дополнительные параметры их конструкторам:

    enum Color2 {
        Red;
        Green;
        Blue;
        Grey( v : Int );
        Rgb( r : Int, g : Int, b : Int );
    }

Таким образом, существует бесконечное множество вариантов Color2', хотя конструкторов всего пять. Все приведенные ниже значения являются значениями Color2:

    Red;
    Green;
    Blue;
    Grey(0);
    Grey(128);
    Rgb( 0x00, 0x12, 0x23 );
    Rgb( 0xFF, 0xAA, 0xBB );

Также, мы можем создать рекурсивный тип, например, добавим alpha:

    enum Color3 {
        Red;
        Green;
        Blue;
        Grey( v : Int );
        Rgb( r : Int, g : Int, b : Int );
        Alpha( a : Int, col : Color3 );
    }

Следующие примеры являются валидными значениями Color3:

    Alpha( 127, Red );
    Alpha( 255, Rgb(0,0,0) );

Обратите внимание, что enum - неизменяемый тип, т.е. параметры конструктора Enum доступны только для чтения. Мы не можем и не должны менять их после инициализации.

Switch по значению Enum

Выражение switch имеет специальное поведение при использовании его с enum. В случае если не задан вариант default, компилятор проверит что все возможные конструкторы enum проверены в switch, и, если нет - сгенерирует сообщение об ошибке. Например, вспомним наш первый enum - Color:

    switch( c ) {
        case Red: 0xFF0000;
        case Green: 0x00FF00;
    }

Этот код будет причиной ошибки компиляции, указывающей на то, что конструктор Blue не был проверен. В данном примере, вы могли бы либо добавить обработку варианта, либо добавить вариант “default” для общего случая. Это может быть полезно, т.к. когда вы добавляете новые конструкторы в ваш enum, ошибка компилятора известит вас о местах в вашей программе, где новый конструктор должен быть обработан.

Switch с параметрами конструктора

Если конструктор enum имеет параметры, они обязаны быть перечислены как имена переменных в варианте switch. Таким образом, все переменные будут доступны локально в выражении case и иметь тип, соответствующий типу параметра конструктора enum. Например, используя enum Color3:

    class Colors {
        static function toInt( c : Color3 ) : Int {
            return switch( c ) {
                case Red: 0xFF0000;
                case Green: 0x00FF00;
                case Blue: 0x0000FF;
                case Grey(v): (v << 16) | (v << 8) | v;
                case Rgb(r,g,b): (r << 16) | (g << 8) | b;
                case Alpha(a,c): (a << 24) | (toInt(c) & 0xFFFFFF);
            }
        }

    }

Использование switch - единственно возможный способ получить параметры конструктора enum.

Параметры типов Enum

Enum так же может иметь параметры типа (type parameters). Синтаксис аналогичен параметрам типа у классов. Вот небольшой пример параметризованного “связанного списка”, реализованного с помощью enum, хранящего ячейки:

    enum Cell<T> {
        Empty;
        Cons( item : T, next : Cell<T> );
    }

    class List<T> {
        var head : Cell<T>;

        public function new() {
            head = Empty;
        }

        public function add( item : T ) {
            head = Cons(item,head);
        }

        public function length() : Int {
            return cell_length(head);
        }

        private function cell_length( c : Cell<T> ) : Int {
            return switch( c ) {
            case Empty : 0;
            case Cons(item,next): 1 + cell_length(next);
            }
        }

    }

Использование enum и классов вместе может быть очень мощным инструментом во многих случаях.

Использование Enums в качестве значений по умолчанию для параметров

Из-за того, что значения Enum на самом деле создаются из конструктора, они не являются константными значениями и поэтому не могут быть использованы в качестве значения по умолчанию для параметров функций. Однако, есть простой обходной путь:

    enum MyEnum {
        MyFirstValue;
        MySecondValue;
    }

    class Test {
        static function withDefaultValuesOnParameters(?a : MyEnum) {
            if(a == null)
                a = MyEnum.MyFirstValue;
        }
    }

Преобразовнаие Enum в строки и обратно


Метод Type.createEnum() позволяет воссоздать значение перечисления из строки:
    enum EColor {
        Red;
        Green;
        Blue;
    }

    // To string:
    var string = Std.string(EColor.Blue);

    // From string:        
    var color:EColor = Type.createEnum(EColor, string);

Равенство Enum’ов

Метод Type.enumEq() позволяет проверить равенство двух значений enum. (Оператор == не гарантирует это. Хотя он и будет работать для конструкторов без аргументов, но может не сработать для конструкторов с аргументами. В итоге можно сказать: для правильного поведения, всегда используйте Type.enumEq())

Также учтите, что хотя Type.enumEq и проверяет аргументы конструктора, он делает это простой проверкой равенства, не проверяя вложенные структуры, поэтому, например, массивы в конструкторах enum будут проверены не по содержимому, а просто по ссылке на объект. Используйте Pattern Matching для сопоставления по структуре.

Примечание


Перечисления в Haxe очень похожи на tagged union и позволяют описать и обработать любой “вариант” результата выполнения функции. Так как все варианты Enum должны быть указаны в операторе switch, они становятся очень полезны для полноценного определения поведения функции и удостоверения, что все варианты поведения обработаны.

«« Параметры классов - Пакеты и импорт »»

version #19585, modified 2013-07-17 08:54:26 by nadako