Defines a user-defined enumeration (also known as an enumerated type or enum). An enum is made
up of a list of strongly typed, named constants called members. The value of a
variable defined as an enum is restricted to the list of members defined for that
enum. Some of the advantages of enums are that they are self-documenting, strongly
typed, and validated at compile time (i.e., the compiler verifies that a variable
defined as an enum can only contain values defined by that enum).
In addition to being named, each member also has an underlying numeric value associated with
it. By default, the compiler sets those associated values automatically. Generally,
you do not need to worry about the numerical values, but you can specify one or more
of the values explicitly if desired.
You can define two types of enum. In the standard version, a variable defined as an enum can
only be assigned one value at a time. You can also define a flag enum. Any variable
defined as a flag enum can be assigned any combination of the members (also referred
to as "flags" for flag enums) at one time. You can perform operations on flag enum
variables to set and unset one or more of the flags.
All enums implicitly inherit from one of two classes. The single-value enums inherit from
Progress.Lang.Enum, and flag enums inherit from
Progress.Lang.FlagsEnum, which is a subclass of
Progress.Lang.Enum. See the Progress.Lang.Enum class
and Progress.Lang.FlagsEnum class entries for more information.
Syntax
ENUM enum-type-name [FLAGS]:
DEFINE ENUM enum-member-definition
[enum-member-definition] ...
END [ENUM].
|
-
enum-type-name
- Defines the type name for a user-defined enum. You can specify an ABL enum name in a way
similar to how you specify an ABL class type. All the requirements and
restrictions on how to specify an ABL class type apply to an enum. See the
CLASS statement and Type-name syntax entries for more details.
- FLAGS
- Defines the enum as a flag enum, with the defined members acting as flags.
This allows you to set one or more flags into the same variable. You can
define up to 64 distinct members to be used as flags. You can also define a
flag as a combination of other flags, and a flag defined this way does not
count as a distinct flag.
- enum-member-definition
- Defines a member using the following syntax:
member-name [ = {member-name [, member-name]... }
| constant ]
|
- member-name
- Specifies the name of the member. Each member name must be
unique among the names of all members defined in the enum as
well as all properties, events, and variable data members that
are defined in the class hierarchy and accessible to the
defining class. The rules for member names follow the same rules
that are in place for variable and property names. Member names
are case insensitive and can be reserved keywords.
- member-name
[, member-name]
- Assigns the same numeric value as a previously defined member.
In this way, you can define multiple members as equivalent with
the same underlying value. For flag enums, you can also equate
an member with multiple, previously defined members, entered as
a comma-delimited list. See the Examples section for more
information.
- constant
- Defines the numeric value associated with the member. The value
must be a constant of type INT64. The value can be represented
in hexadecimal notation (for easier readability with flag enums,
for example).
- If the first member is not explicitly assigned a value, the
compiler starts with a value of 1. For each subsequent member
not assigned a value, the compiler assigns a value 1 larger than
the previous member for a single-value enum, or the next power
of 2 for a flag enum.
Note: Flag enums are the primary use case
for explicitly setting the underlying values of members.
Progress recommends that, when defining a flag enum, you
always explicitly define a member with a value of
0.
- END [ENUM]
- Specifies the end of the enum definition. You must end the enum definition with the END
statement.
Examples
The following example shows how to define an enum with the four cardinal directions as its named values:
ENUM Direction:
DEFINE ENUM North
South
East
West.
END ENUM.
|
The next example shows how to define an member as equivalent to another:
ENUM Direction:
DEFINE ENUM North
Default = North
South
East
West.
END ENUM.
|
In this case, both North and Default have an underlying
value of 1.
The following is an example of a flag enum, including the member ReadWrite
being defined as a combination of previously defined members:
ENUM Permission FLAGS:
DEFINE ENUM None = 0
Read
Write
Update = Write
ReadWrite = Read,Write
Create
Delete
Execute.
END ENUM.
|
For flag enums, it is recommended that you include an member with an underlying value of 0,
representing no flags set. Because the compiler automatically starts with a value of
1 for both single-value and flag enums, you must define an member with a value of 0
explicitly.
The syntax for assigning a value to a variable defined as an enum is similar to the syntax
used to assign values to static data members of a class:
enum-type-name:member-name
|
Using the Direction enum from the first example, this code fragment defines
myDirection as a variable of type Direction
and assigns myDirection the member South:
DEFINE VARIABLE myDirection AS Direction.
myDirection = Direction:South.
|
This syntax cannot be used to set multiple flags for a variable defined as a flag enum. To set
multiple flags, you need to use bitwise operators or the SetFlag(
), ToggleFlag( ), and UnsetFlag( )
methods, as described in the Notes section of this entry.
Members are read-only, so the following code will not compile:
Direction:South = myDirection.
|
Like other uninitialized class references, variables defined as enums have an initial
value of the unknown (?) value.
Notes
- Each member represents an instance of an object of the type of enum that defines
them. When you reference a member, you get an instance that is maintained by the
AVM.
- Because enums are implicitly class based, you can use them in many of the same
ways you can use a class type or instance. You can define variables, parameters,
return types, and class-based properties as an enum. These data elements can
then hold a reference to an enum instance. For example, using the
Permission enum defined in the Examples, the following code
fragments are all valid:
METHOD PUBLIC Permission SomeOperation (INPUT perm AS Permission):
END.
PROCEDURE proc:
DEFINE INPUT-OUTPUT PARAMETER perm AS Permission.
END.
FUNCTION func1 RETURNS Permission (OUTPUT perm AS Permission):
END.
|
You can explicitly pass members as INPUT parameters using the
enum-type-name:member-name
syntax, but because the member itself cannot be modified, it cannot be
passed as an OUTPUT or INPUT-OUTPUT parameter. For example, the following is
not allowed:
RUN proc1 (INPUT-OUTPUT Permission:Read).
|
You can also assign an enum object reference to a temp-table
field defined as the Progress.Lang.Object class type, but you cannot assign
an enum object reference to a database table field. For more information on
object references in general, see the reference entry for a Class-based object reference.
- Enums must be defined in a .cls file by themselves, not with other class or
interface definitions.
- Although enums are implicitly class based, you can not instantiate an enum using
NEW. You must use the type-name syntax shown in the Examples section to access
the members defined in an enum, or use the GetEnum( ) method to
get an enum instance. The method takes an enum member name as a CHARACTER or the
underlying numeric value of a member as an INT64 and returns an enum instance.
For example, both of the following lines of code create an enum instance using
the Direction enum type, and in both cases,
myDirection is assigned the member South:
DEFINE VARIABLE myDirection AS Direction.
myDirection = Direction:GetEnum("South").
myDirection = Direction:GetEnum(2).
|
This method is not part of the implementation of
Progress.Lang.Enum, but it is automatically available for
all built-in and user-defined enum types. For full details, see the GetEnum( ) method entry.
- All enums are final. They also cannot explicitly inherit from another class or
implement interfaces.
- Enums are serializable. You can pass parameters defined as enums between an ABL
client and an AppServer.
- Much of the functionality for classes is also supported for enums, including
package names, the USING statement, reflection, and assignment and casting
rules. Unlike classes, however, enums cannot have data members, properties,
events, or user-defined methods.
- You can define variables, properties, and parameters using the AS CLASS phrase.
For example:
DEFINE VARIABLE myDirection AS CLASS Direction.
|
- In the original Direction enum example, the underlying values
for the members are automatically set by the compiler as 1 for
North, 2 for South, and so on. However,
you can change this pattern by explicitly setting one or more of the numerical
values. For example:
ENUM Direction:
DEFINE ENUM North
South
East = 4
West.
END ENUM.
|
In this case, the underlying values for the members are still
automatically set by the compiler as 1 for North, 2 for
South. East is explicitly set as 4, so
the compiler sets the value of West as one larger than the
previous member, which in this case will be 5. The same rules hold true for
flag enums, except that the compiler assigns the next power of 2 to each
member that does not have its underlying value explicitly set.
-
When you have to explicitly set the underlying value of an member, you can
use hexadecimal notation, particularly for ease of readability with flag
enums. Following is the Permission enum with the underlying
values explicitly set:
ENUM Permission FLAGS:
DEFINE ENUM None = 0
Read = 0x01
Write = 0x02
Update = 0x02
ReadWrite = 0x03
Create = 0x04
Delete = 0x08
Execute = 0x10.
END ENUM.
|
- If desired, you can split the defined list of members in an ENUM statement into
two or more DEFINE ENUM statements for readability. For example:
ENUM Direction:
DEFINE ENUM North
South.
DEFINE ENUM East
West.
END ENUM.
|
The same rules for how the compiler assigns the underlying numerical
values still apply, i.e., in the example above, South has
an underlying value of 2, and East has an underlying value
of 3. In other words, the underlying numerical values will be assigned
according to the order of the members, regardless of whether or not the
member list is split into multiple DEFINE ENUM statements.
Note: Although you can split the list of members into separate DEFINE
ENUM statements, the list of members for a given enum must be completely
defined in a single ENUM statement. Additional members cannot be added to a
defined enum in a separate ENUM statement.
-
The following comparison operators can be used with enums, as long as the two
expressions being compared are both defined as the same enum type:
- EQ (=)
- NE (<>)
- GT (>)
- LT (<)
- GE (>=)
- LE (<=)
When used with expressions that are enum types, the operators compare the
underlying numerical values of the members. Comparisons can be made using
either single-value enums or flag enums. Using the previously defined
Direction enum, this code fragment shows a comparison
and results in the message being displayed:
DEFINE VARIABLE myDirection AS Direction.
myDirection = Direction:South.
IF myDirection NE Direction:North THEN
MESSAGE "The current heading is not north."
VIEW-AS ALERT-BOX.
|
You can only use the comparison operators for two expressions defined as the
same enum type.
The CompareTo( ) method of Progress.Lang.Enum also allows
you to compare enum instances based on the members' numeric values. See the
CompareTo( ) method entry for details.
- The CASE statement also supports the use of enums. For example, using the
Direction enum defined in the Examples section:
DEFINE VARIABLE myDirection AS Direction.
myDirection = Direction:South.
CASE myDirection:
WHEN Direction:North THEN
MESSAGE "Your current direction is north.".
WHEN Direction:South THEN
MESSAGE "Your current direction is south.".
OTHERWISE
MESSAGE "Your current direction is east, west, or unknown.".
END CASE.
|
The expressions in the WHEN phrases must be members from the enum type
in the CASE statement.Note: You can use flag enums with the CASE statement, but
be aware that the WHEN phrases check for equality of the members strictly
based on numeric values, not as combinations of multiple flags. For example,
in the following code, the two WHEN statements for
Permission:Read and
Permission:Write
will not fire for the given value of
myPermission; the
OTHERWISE statement will.
DEFINE VARIABLE myPermission AS Permission.
myPermission = Permission:ReadWrite.
CASE myPermission:
WHEN Permission:Read THEN
MESSAGE "You can read data.".
WHEN Permission:Write THEN
MESSAGE "You can write data.".
OTHERWISE
...
END CASE.
|
- The following bitwise operators can be used with flag enums. Each operator does
a bitwise comparison of two enum expressions and returns a new enum instance as
a result:
- Bitwise AND: Check the state of a particular flag.
- Bitwise OR: Set a flag.
- Bitwise XOR (exclusive OR): Toggle a flag.
- Bitwise NOT (complement): Turn off a flag.
Caution:
Because
bitwise NOT turns off flags, it is possible to end up with an enum
instance with no flags set, i.e., a value of 0. Always defining an
member with value 0 is recommended to avoid an enum expression that
doesn't correspond to any defined members.
As with the logical comparison operators, both expressions must be the same
enum type. If they are not, the code will not compile.Using the
Permission enum from the Examples section, the
following examples show use cases of the bitwise operators:
DEFINE VARIABLE myPermissions AS Permission.
/* myPermissions is set with a Permission instance with the Read and
Write flags set. */
myPermissions = Permission:Read OR Permission:Write.
/* All flags are set with the possible exception of Create, which is
toggled. */
myPermissions = myPermissions XOR Permission:Create.
/* Ensures that the Create flag is not set. */
myPermissions = myPermissions AND NOT Permission:Create.
/* All flags are set except Create. */
myPermissions = NOT Permission:Create.
/* Checks to see if Read is set. */
IF (myPermissions AND Permission:Read) = Permission:Read THEN ...
/* Three additional ways to check if Read is set. */
IF (myPermissions AND Permission:Read):Equals(Permission:Read) THEN ...
IF myPermissions:IsFlagSet(Permission:Read) THEN ...
IF (myPermissions AND Permission:Read) NE Permission:None THEN ...
|
The order of operator precedence for bitwise and logical operators is
the following:
- bitwise NOT
- logical NOT
- bitwise AND
- bitwise XOR
- bitwise OR
- logical AND
- logical OR
In practice, the order of precedence for bitwise and logical operators
are independent because the bitwise operators return an enum instance and
the logical operators return a logical. Code like the following will not
compile because
enum1 AND enum2 returns an enum instance,
and a logical is required for the
OR part of the
statement:
IF enum1 AND enum2 OR iCount <> 1...
|
See the Expression entry for the full table
of operator precedence.
- In addition to the bitwise operators, there are three methods that you can use
to set and unset flags: the ToggleFlag( ) method,
the SetFlag( ) method, and the UnsetFlag( ) method. These methods are not part of
the implementation of Progress.Lang.FlagsEnum, but they are
automatically available for all built-in and user-defined flag enums.
- The MINIMUM and MAXIMUM functions do not support enums.
- The STRING and QUOTER functions return a member name for enum-valued
expressions. For flag enum-valued expressions with multiple flags set, STRING
returns a comma-separated list of member names. Similarly, PUT will send the
appropriate member name(s) to the output destination.
- The INTEGER, INT64, and DECIMAL functions return the underlying numeric value of
an member for enum-valued expressions. Given that the numeric values of an
member are of type INT64, it is possible the INTEGER function will raise an
error with an member if the numeric value cannot be represented as an INTEGER.
- Calling DELETE OBJECT on an enum instance will be silently ignored because enum
instances are maintained by the AVM. Enums also do not appear in the
SESSION-FIRST-OBJECT/SESSION-LAST-OBJECT chain.
See also
AND operator (bitwise), Assignment (=) statement, Class-based object reference, DYNAMIC-ENUM function, NOT operator (bitwise), , OR operator (bitwise), Progress.Lang.Enum class, Progress.Lang.FlagsEnum class, Type-name syntax, USING statement, XOR operator (bitwise)