An
enumerated type defines an ordered set of values by simply listing identifiers
that denote these values. The values have no inherent meaning. To declare an
enumerated type, use the syntax
type typeName = (val1, ..., valn)
where typeName and each val
are valid identifiers. For example, the declaration
type
Suit = (Club, Diamond, Heart, Spade);
defines an enumerated type called Suit
whose possible values are Club, Diamond, Heart, and Spade,
where Ord(Club) returns 0, Ord(Diamond) returns 1, and so forth.
When you
declare an enumerated type, you are declaring each val to be a constant
of type typeName. If the val identifiers are used for another
purpose within the same scope, naming conflicts occur. For example, suppose you
declare the type
type
TSound = (Click, Clack, Clock);
Unfortunately,
Click is also the name of a method defined for TControl and all
of the objects in the VCL and/or CLX that descend from it. So if you’re writing
an application and you create an event handler like
procedure
TForm1.DBGrid1Enter(Sender: TObject);
var
Thing: TSound;
begin
...
Thing := Click;
...
end;
you’ll get a compilation error; the
compiler interprets Click within the scope of the procedure as a
reference to TForm’s Click method. You can work around this by
qualifying the identifier; thus, if TSound is declared in MyUnit,
you would use
Thing
:= MyUnit.Click;
A better
solution, however, is to choose constant names that are not likely to conflict
with other identifiers. Examples:
type
TSound = (tsClick, tsClack, tsClock);
TMyColor = (mcRed, mcBlue, mcGreen,
mcYellow, mcOrange);
Answer = (ansYes, ansNo, ansMaybe);
You can
use the (val1, ..., valn) construction directly in variable
declarations, as if it were a type name:
var
MyCard: (Club, Diamond, Heart, Spade);
But if
you declare MyCard this way, you can’t declare another variable within
the same scope using these constant identifiers. Thus
var
Card1: (Club, Diamond, Heart, Spade);
var
Card2: (Club, Diamond, Heart, Spade);
generates a compilation error. But
var
Card1, Card2: (Club, Diamond, Heart, Spade);
compiles cleanly, as does
type
Suit = (Club, Diamond, Heart, Spade);
var
Card1: Suit;
Card2: Suit;
By
default, the ordinalities of enumerated values start from 0 and follow the
sequence in which their identifiers are listed in the type declaration. You can
override this by explicitly assigning ordinalities to some or all of the values
in the declaration. To assign an ordinality to a value, follow its identifier
with = constantExpression, where constantExpression is a constant expression that evaluates to an
integer. For example,
type
Size = (Small = 5, Medium = 10, Large = Small + Medium);
defines a type called Size whose
possible values include Small, Medium, and Large, where
Ord(Small) returns 5, Ord(Medium) returns 10, and Ord(Large) returns 15.
An
enumerated type is, in effect, a subrange whose lowest and highest values
correspond to the lowest and highest ordinalities of the constants in the
declaration. In the example above, the Size type has 11 possible values
whose ordinalities range from 5 to 15. (Hence the type array[Size] of
Char represents an array of 11 characters.) Only three of these values have
names, but the others are accessible through typecasts and through routines
such as Pred, Succ, Inc, and Dec. In the following
example, anonymous values in the range of Size are assigned to the
variable X.
var
X: Size;
X :=
Small; //
Ord(X) = 5
X :=
Size(6); // Ord(X) = 6
Inc(X); //
Ord(X) = 7
Any value
that isn’t explicitly assigned an ordinality has ordinality one greater than
that of the previous value in the list. If the first value isn’t assigned an
ordinality, its ordinality is 0. Hence, given the declaration
type
SomeEnum = (e1, e2, e3 = 1);
SomeEnum has only two possible values: Ord(e1) returns 0, Ord(e2) returns 1, and Ord(e3) also returns 1; because e2 and e3 have the same ordinality, they represent the same value.
枚举类型定义了一个有序的值的集合,这些值简单地用标识符列表表示,并且不需要有固定含义。声明枚举类型的语法是:
type typeName = (val1, ..., valn)
这里的类型名typeName和每个值val都是有效标识符。例如,声明
type
Suit = (Club, Diamond, Heart, Spade);
定义了一个叫做Suit的枚举类型,Suit类型中可能的值是Club、Diamond、Heart和Spade,这里,Ord(Club)返回0,Ord(Diamond)返回1,等等。
声明枚举类型时,可以声明每个值val作为类型typeName的常量。如果val标识符在相同的作用域中已经用于其他含义,那么将发生命名冲突。例如,假定声明了类型
type
TSound = (Click, Clack, Clock);
不幸的是,Click也已作为TControl极其才VCL和/或CLX中的所有后裔的方法名被声明。因此,如果编写一个应用程序并创建一个事件句柄如
procedure
TForm1.DBGrid1Enter(Sender: TObject);
var
Thing: TSound;
begin
...
Thing := Click;
...
end;
这将导致编译错误,因为编译器将把这里的Click作为对TForm中Click方法的引用来解释。当然,可以通过限定词来回避,这里假如TSound是在单元MyUnit中声明的,那么可以写成
Thing
:= MyUnit.Click;
更好的解决办法是,选择不会与其他标识符冲突的常量名称。例如:
type
TSound = (tsClick, tsClack, tsClock);
TMyColor = (mcRed, mcBlue, mcGreen,
mcYellow, mcOrange);
Answer = (ansYes, ansNo, ansMaybe);
可以使用(val1, ..., valn)结构直接声明枚举类型,例如
var
MyCard: (Club, Diamond, Heart, Spade);
但这样就不能在相同的作用域中再声明另一个变量使其具有相同的标识符。如
var
Card1: (Club, Diamond, Heart, Spade);
var
Card2: (Club, Diamond, Heart, Spade);
这将产生编译错误。而声明
var
Card1, Card2: (Club, Diamond, Heart, Spade);
可以通过编译,相当于
type
Suit = (Club, Diamond, Heart, Spade);
var
Card1: Suit;
Card2: Suit;
缺省情况下,枚举值的序号从0开始,并且接下来的序列对应类型声明中相应的标识符。可以通过对部分或全部枚举值明确指定序号来忽略缺省序号。要对枚举值指定序号,则要再标识符后跟随 = constantExpression,这里 的constantExpression是一个可以计算得到整数的常量表达式。例如
type
Size = (Small = 5, Medium = 10, Large = Small + Medium);
定义了一个叫做Size的枚举类型,该类型可能的值包括Small、Medium和Large,这里Ord(Small)返回5,Ord(Medium)返回10,Ord(Large)返回15。
实际上,一个枚举类型就是一个子界,该子界的最小值和最大值对应于声明中常量的最小序号和最大序号。再上面的例子中,Size类型有11个可能的值,这些值的范围是5到15。(因此,类型array[Size] of Char表示的是一个含有11个字符的数组。)在这些值中,只有三个有名字,但其他的值通过类型转换和一些例程(如Pred、Succ、Inc、Dec)也是可访问的。下面的例子中,在Size类型范围之内的匿名值被赋给了变量X。
var
X: Size;
X :=
Small; //
Ord(X) = 5
X :=
Size(6); // Ord(X) = 6
Inc(X); //
Ord(X) = 7
在枚举类型声明中,任何没有明确给出序号的值,其缺省序号比其前驱值大1。如果第一个值没有被指定序号,那么其序号为0。因此,对于给出的声明
type
SomeEnum = (e1, e2, e3 = 1);
枚举类型SomeEnum仅有两个可能的值:Ord(e1) 返回0,Ord(e2)返回1,并且Ord(e3)也返回1,因为e2和e3具有相同的序号,所以他们表示相同的值。