Enumerations

Posted by guilmori on 03-May-2010 09:21

<prepostranting>

Again, loosing some precious time on plumbing code trying the re-invent the wheel...

</prepostranting>

I've seen some pseudo enums implementation using static properties, as in this thread:

http://communities.progress.com/pcom/message/59339#59339

What I don't like about this kind of enum is that it is not type safe from the consumer point of view.

If a method expect a SortDirection enum as an input param, there is no indication of this expectation to the consumer, all it sees is an integer parameter.

Moreover, consumer can send other values not defined in the enum.

So I did try to push this idea a little bit further by using class type instances instead of primitive types for the enum values.

Here is an example:


ROUTINE-LEVEL ON ERROR UNDO, THROW.


CLASS enum.SortDirection:

   /* Must use PROTECTED since PRIVATE gives an error ... ?? */

      DEF PROTECTED STATIC PROPERTY AscendingDirectionValue  AS INTE NO-UNDO INIT 0 GET.
   DEF PROTECTED STATIC PROPERTY DescendingDirectionValue AS INTE NO-UNDO INIT 1 GET.
  
   DEF PROTECTED STATIC VAR moAscendingDirection AS enum.SortDirection NO-UNDO.
   DEF PUBLIC STATIC PROPERTY AscendingDirection AS enum.SortDirection NO-UNDO
      GET(): IF moAscendingDirection = ? THEN
                moAscendingDirection = NEW enum.SortDirection(AscendingDirectionValue).
             RETURN moAscendingDirection.
      END GET.

   DEF PROTECTED STATIC VAR moDescendingDirection AS enum.SortDirection NO-UNDO.
   DEF PUBLIC STATIC PROPERTY DescendingDirection AS enum.SortDirection NO-UNDO
      GET(): IF moDescendingDirection = ? THEN
                moDescendingDirection = NEW enum.SortDirection(DescendingDirectionValue).
             RETURN moDescendingDirection.
      END GET.

   METHOD PUBLIC STATIC enum.SortDirection GetEnumInstance(viEnumValue AS INTE):

      CASE viEnumValue:
         WHEN AscendingDirectionValue   THEN RETURN AscendingDirection.
         WHEN DescendingDirectionValue  THEN RETURN DescendingDirection.
         OTHERWISE UNDO, THROW NEW PROGRESS.Lang.AppError(SUBST("Invalid enumeration value for SortDirection : &1.":U, viEnumValue), 1).
      END CASE.

   END METHOD.


   DEF PUBLIC PROPERTY EnumValue AS INTE NO-UNDO GET. PRIVATE SET.


   CONSTRUCTOR PRIVATE SortDirection(INPUT viEnumValue AS INTE):
      EnumValue = viEnumValue.
   END CONSTRUCTOR.

   METHOD PUBLIC CHAR ToStringForQuery():

      CASE EnumValue:
         WHEN AscendingDirectionValue  THEN RETURN " ":U.
         WHEN DescendingDirectionValue THEN RETURN " DESC ":U.
      END CASE.

   END METHOD.

END CLASS.

So I was wondering what are your thoughts and if anyone has successfully implemented an approach like this ?

All Replies

Posted by Peter Judge on 03-May-2010 10:46

I use a general EnumMember class (below) which I use in my Enum classes. You get stronger type-safety - I pass them around as EnumMember and not integer or character - but not 100% since all enum members are EnumMember, although you could certainly just create a (in my example) SessionClientTypeEnumMember class and use that for the enum members. I usually use the member instance for comparison purposes (and not the name or value).

class OpenEdge.Lang.EnumMember:

    define public property Name as character no-undo get. protected set.

    define public property Value as integer no-undo get. protected set.

end class.

class OpenEdge.Lang.SessionClientTypeEnum final:   
    define static public property ABLClient as EnumMember no-undo get. private set.
    define static public property WebClient as EnumMember no-undo get. private set.
    define static public property AppServer as EnumMember no-undo get. private set.
    define static public property WebSpeed  as EnumMember no-undo get. private set.
    define static public property Other     as EnumMember no-undo get. private set.
    define static public property CurrentSession as EnumMember no-undo get. private set.
   
    constructor static SessionClientTypeEnum():
        SessionClientTypeEnum:ABLClient = new EnumMember('4GLCLIENT').
        SessionClientTypeEnum:WebClient = new EnumMember('WEBCLIENT').
        SessionClientTypeEnum:AppServer = new EnumMember('APPSERVER').
        SessionClientTypeEnum:WebSpeed  = new EnumMember('WEBSPEED').
        SessionClientTypeEnum:Other     = new EnumMember('OTHER').
       
        SessionClientTypeEnum:CurrentSession = SessionClientTypeEnum:EnumFromString(session:client-type).
    end constructor.   

end class.

-- peter

Posted by guilmori on 03-May-2010 12:15

pjudge wrote:

... although you could certainly just create a (in my example) SessionClientTypeEnumMember class and use that for the enum members.


Why not use the same class (SessionClientTypeEnum) for your member instances ?

And maybe have all your enum class inherits EnumMember ... but, is all your enum values always integer ?

BTW, I could not instantiate my enum member instances in a static constructor since when running in the editor, it does some clean-up at the end of the execution, destroying the member instances, and then the static constructor is never executed again.

Posted by Peter Judge on 03-May-2010 13:03

Why not use the same class (SessionClientTypeEnum) for your member instances ?

That's not a bad idea at all. I'll try it.

And maybe have all your enum class inherits EnumMember ... but, is all your enum values always integer ?

Yeah, although enums in .Net are by default integers too (though you can override them). But this is somewhat of a leftover from earlier attempts: I rarely use the integer value for anything anymore: I simply use the instance reference ("handle") for comparisons etc. It is useful to have the integer value is if you're working with flag enums, or when I'm mapping to ABL constructs.

BTW, I could not instantiate my enum member instances in a static constructor since when running in the editor, it does some clean-up at the end of the execution, destroying the member instances, and then the static constructor is never executed again.


Yeah, this is because of the cleanup that the procedure editor does.

-- peter

"one time" had two times
Message was edited by: Peter Judge

This thread is closed