Enumerations, also called enumerated types or just enums are a computer programming concept that occurs in many procedural programming languages.
Enums have made it into most object-oriented languages that evolved out of these procedural languages. C and C++ have them. C# has them. Java does not (update: enums are being introduced into Java 1.5).
In my opinion, in Pascal and its object-oriented offshoot Delphi, enums reach their full potential. The language has some flaws and shortcomings, but the usage of enums is not equalled elsewhere in the C family of languages.
An enum is a user-defined type that allows selection from a small number of listed options. When defining the type, you must enumerate the possible values, in Webster's sense of to mention one by one.
Over using a raw integer value an enumerated type provides type safety, range checking, readability and abstraction.
Confused by that? Let's look at some code, starting with Java. Java's theory is that if a non-object-oriented type isn't a built in, atomic type, it is just another complexity that you don't need, and it should be done via an object type. This theory works, mostly. Enums are the single biggest flaw in it.
The designers of Java decided that enums could be subsumed by their object model. You could do that verbose, expensive kludge, but nobody does that in practice. In true C programmer style they use simple, efficient integer constants. This kind of code is common in the Java class library:
public static final int DATE_PART_MONTH = 3;
public static final int DATE_PART_YEAR = 4;
.. and elsewhere ...
public static final int COLOR_BLUE = 11;
public static final int COLOR_GREEN = 12;
This falls short on type safety: You can do
int myColour= DATE_PART_MONTH;
is not to do with dates.
This falls short on range checking: You can do
int foo = DATE_PART_MONTH + DATE_PART_YEAR;
when 7 is not a valid date part.
This falls short on readability. You can do
when it’s not obvious what colour 5 means.
This falls short on abstraction: Colors are not integers. Sure they are implemented in terms of integers, but this does not encourage a distance between the problem space and the solution space.
Let me show you how enums can work in Pascal, and any other language that has fully learned what Pascal has to offer in this regard (this exludes Java, which doesn't have enums at all, and C, which can’t tell them from ints).
This defines an emerated type with 8 possible values corresponding to eight colours.
TColour = (eRed, eGreen, eBlue, eWhite, eBlack, eOrange, ePurple, eBrown);
This declares a variable of that type.
An enumerated type is not an integer, or else you could cause errors by putting in an out-of-range value, or a value from the wrong enumeration.
An enumerated type is not an integer, though it is ordinal. Ordinal? This means simply that like the type
char, and unlike the type
float, it has a finite number of values, and that you can order them, with a first one, a last one, and each value has the notion of a predecessor and successor value (not defined for the lowest and highest value respectively). It is an ordered set.
eRed is not the same as zero, and
eGreen is not the same as 1, but
Ord(eRed) = 0 and
Ord(eGreen) = 1.
eRed + 1 is not allowed, but
Succ(eRed) = eGreen.
This gives you type safety. You cannot do
MyColour := 1; or
MyColour := eDatePartMonth;
(assuming that date parts will have their own enumerated type)
Yeah, this is implemented in terms of simple efficient integers, but so are lots of valuable abstractions. The compiler catches the errors, and gives you efficent machine code from source code in the problem domain, not in the level of integers.
With this implementation detail in mind, we can put some limits on enums: if you want the enum variable to fit into one byte, it cannot have more than 256 values. However using a 16 or 32 bit word is more common for performance reasons. This allows an enum to have as many values as you could want. Once you get so many different values that you can't name them all individually, then perhaps an enum is not the best design choice.
Hard, unsafe, type casting is occasionally necessary.
1 is not equal to
eGreen , but
TColour(1) is equal to
There is no need to ever assign numeric values to enums except to interoperate with broken languages that, lacking sets over enums, abuse them as bitmasks.
For instance, some C code is written as
enum modes = (MODE_READ = 1, MODE_WRITE = 2, MODE_EXEC = 4 );
A variable would then contain an XOR of these modes, each bit either on or off. This enum is not a list of possible values anymore. It is using your knowledge of integers and bits to shorthand a bitmask.
Let's look at how that would go in Pascal, if interoperating with an OS weren't an issue.
TMode = (MODE_READ, MODE_WRITE, MODE_EXEC);
TModeSet = set of TMode;
MyMode := [MODE_READ, MODE_EXEC] ;
If MODE_EXEC in MyMode then
it may be true that a set is really just a bitmask with one bit for each possible value of the ordinal type over which it is defined, and thus must be implemented in terms of ints and bitmasks anyway. The machine code will be the same as the C style stuff. True, but set operators are still easier to read, and the compiler gets the easy bugs for you. Set membership may be implemented in terms of XOR, but that doesn’t mean that they are the same thing.
With that implementation detail in mind though, you can see that a set type will be one bit wide for each value in the base type. e.g. a variable of a set type over an enum with 8 elements must have eight distinct flags, and will just fit into a byte. For instance, Delphi specifies that when making a set from an ordinal type, the base type can have no more that 256 elements, and all of these must have ord from zero to 255.