New Fun Blog – Scott Bilas

Take what you want, and leave the rest (just like your salad bar).

Update 2: Faking Enums in AS3

with 9 comments

[Update: the final version of my enum class is here.]

Well, I couldn’t help but improve on the fake enums just a bit more (see previous article and the original one).

Because the system does involve some repetition in its pattern, it’s very likely that a copy-paste error is going to result in unwanted behavior not detected by the compiler. In fact, I did that very thing myself as I was working on an app I’m doing on the side.

Consider this code:

public class EError extends EEnum
{
    {initEnum(EState);} // static ctor

    public static const None            :EState = new EState();
    public static const CantConnect     :EState = new EState();
    public static const VersionMismatch :EState = new EState();
    public static const Unknown         :EState = new EState();
}

Whoops! We have two major classes of mistakes here.

  • The type of each constant is wrong. Hopefully the compiler will eventually catch this if you do write some type-safe code such as var err :EError = EError.None, but this isn’t guaranteed. It’s also not going to be obvious what the problem is.
  • The type sent into initEnum() is wrong. This means that EError’s constants will never get their Text fields set right. Any code that relies on this (such as for logging) will fail, not to mention the difficulty in debugging.Most likely this will get noticed right when it’s super-important: in the middle of a deep debugging session where you absolutely need to know what a certain enum is set to. Can work around it, but let’s try to avoid that happening in the first place.

Another issue I’d like to fix is to make the Text field read-only so it doesn’t get modified by accident. And we might as well rename it to a better name like Name while we’re at it.

So here’s my final solution:

import flash.utils.describeType;

public class EEnum
{
    public function get Name() :String
        { return _name; }

    public function toString() :String // override
        { return Name; }

    protected static function initEnum(i_type :*) :void
    {
        var type :XML = flash.utils.describeType(i_type);
        for each (var constant :XML in type.constant)
        {
            var enumConstant :EEnum = i_type[constant.@name];

            // if 'text' is already initialized, then we're probably
            // calling initEnum() on the same type twice by accident,
            // likely a copy-paste bonehead mistake.
            if (enumConstant.Name != null)
            {
                throw new Error("Can't initialize '" + i_type + "' twice");
            }

            // if the types don't match then probably have another
            // copy-paste error.
            var enumConstantObj :* = enumConstant;
            if (enumConstantObj.constructor != i_type)
            {
                throw new Error(
                    "Constant type '" + enumConstantObj.constructor + "' " +
                    "does not match its enum class '" + i_type + "'");
            }

            enumConstant._name = constant.@name;
        }
    }

    private var _name :String = null;
}

This has the following fixes:

  • Changed Text to _name, and made it private so only initEnum() will mess with it.
  • Added an accessor for _name called Name for convenience, and threw in a toString() to be more ActionScripty.
  • Added double-init testing. I put this on the constant _name init but could have also had a bool _initted attached to the EEnum. Same result either way, but I think this is a little bit safer because it avoids the (admittedly unlikely) scenario of an enum pulling constants from another. It’s certainly not any harder to understand.
  • Added a test to make sure that each constant initted in the enum is actually of the type requested.

There. Now I’ve done this simple bit of code to death and we can move on. :)

December 16th, 2008 at 6:01 pm

Posted in as3, enum, flex

9 Responses to 'Update 2: Faking Enums in AS3'

Subscribe to comments with RSS or TrackBack to 'Update 2: Faking Enums in AS3'.

  1. Hello,

    I’m a beginner in AS3 but I was thinking of use this signature for the ctor :
    protected static function initEnum(i_type :* extends Enum) :void

    just like I do in Java-like language.

    Could we do something like this in AS3 ?
    because that could add safety to the Enum-Fake : no one could ask the init on something that did not extends EEnum, did it ?

    (N.B. : I apologize for my poor English … )

    larnima

    23 Jun 09 at 2:09 am

  2. I didn’t know you could do that in Actionscript. If you can, then that is definitely a better way. Safer, as you said. Thanks!

    Scott

    23 Jun 09 at 12:53 pm

  3. You can’t do that in AS3, alas. :)

    Jason McIntosh

    20 Aug 09 at 7:30 am

  4. Hello!

    Thank you for this super cool class!

    I was just wondering if someone has figured out a simple way to add an enum iterator?

    I tried to add a static _values array to the EEnum class but it will contain all EEnum subclasses.

    I tried to add a static dictionary with enum class name as a key but then I would have to find out the name of the caller class but there seems to be no way to get the stack trace in flex.

    Miko Korpela

    14 Oct 09 at 10:43 pm

  5. I’m working on a post which addresses the iteration problem. Stay tuned. :)

    Scott

    23 Dec 09 at 7:52 pm

  6. quick additions you my be interested in.

    /** public function parseString(value:String):*
    * Takes a string value and attempts to find
    * a matching constant in the class returns a reference to that constant
    * Throws an error if none found.
    */
    public function parseString(stringValue:String):* {

    // grabs all information about the class
    var classObj = getDefinitionByName(getQualifiedClassName(this));
    var type :XML = flash.utils.describeType(classObj);

    // loops through the constant collecion
    for each (var constant :XML in type.constant) {
    if (stringValue == constant.@name) {
    // found. return reference to constant.
    return classObj[constant.@name];
    }
    }
    // if no match found
    throw new Error(value + ” is invalid string value.”);
    }

    /** public function parseStringCaseInsenstive(value:String):*
    * Takes a string value and attempts to find
    * a matching constant in the class returns a reference to that constant
    * Throws an error if none found.
    */
    public function parseStringCaseInsenstive(stringValue:String):* {

    // grabs all information about the class
    var classObj = getDefinitionByName(getQualifiedClassName(this));
    var type :XML = flash.utils.describeType(classObj);

    // loops through the constant collecion
    for each (var constant :XML in type.constant) {
    // test case-insensitive
    if (stringValue.toUpperCase() == constant.@name.toUpperCase()) {
    // found. return reference to constant.
    return classObj[constant.@name];
    }
    }
    // if no match found
    throw new Error(value + ” is invalid string value.”);
    }

    ken ehrman

    24 Dec 09 at 8:33 am

  7. If you could do that with static functions in the super class that would be a great xmas gift.. I think that the only way is to write a static method in the subclass that calls a superclass method with the subclass as a parameter ( like in the enum init)

    Mikko Korpela

    24 Dec 09 at 11:47 am

  8. On a second thought you could use an enum helper class that is constructed in a static method in the subclass:

    public static function helper():Helper { return super.getHelperFor(ThisClass.class); }

    Helper could have methods for value iteration and enum from string getting etc. For performance reasons all helpers could be cached in a static dictionary in EEnum class.

    Mikko Korpela

    24 Dec 09 at 12:27 pm

  9. Ok new post up with more fun toys for the enum class. This should get it pretty much par with .NET’s enums (minus ‘flag’ ability…maybe will get to that later).

    http://scottbilas.com/2009/12/24/ultimate-as3-fake-enums/

    Scott

    24 Dec 09 at 2:07 pm

Leave a Reply

Note: This post is over a year and a half old. Time moves fast on the internet and this article may be total bunk now! You may want to check later in this blog to see if there is any new information relevant to your comment.

Want to paste some code into your comment? Just wrap it in [code] [/code]. Also, please note that off-topic or overly commercial comments will likely be removed at my discretion.

Switch to our mobile site