A
subrange type represents a subset of the values in another ordinal type (called
the base type). Any construction of the form Low..High, where Low
and High are constant expressions of the same ordinal type and Low
is less than High, identifies a subrange type that includes all values
between Low and High. For example, if you declare the enumerated
type
type
TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);
you can then define a subrange type like
type
TMyColors = Green..White;
Here TMyColors includes the values Green, Yellow, Orange, Purple, and White.
You can use numeric constants and
characters (string constants of length 1) to define subrange types:
type
SomeNumbers = -128..127;
Caps = 'A'..'Z';
When you use numeric or character constants to define a subrange, the base type is the smallest integer or character type that contains the specified range.
The Low..High construction
itself functions as a type name, so you can use it directly in variable
declarations. For example,
var
SomeNum: 1..500;
declares an integer variable whose value
can be anywhere in the range from 1 to 500.
The
ordinality of each value in a subrange is preserved from the base type. (In the
first example above, if Color is a variable that holds the value Green,
Ord(Color) returns 2 regardless of whether Color is of type TColors
or TMyColors.) Values do not wrap around the beginning or end of a
subrange, even if the base is an integer or character type; incrementing or
decrementing past the boundary of a subrange simply converts the value to the
base type. Hence, while
type
Percentile = 0..99;
var
I: Percentile;
...
I :=
100;
produces an error,
...
I :=
99;
Inc(I);
assigns the value 100 to I (unless compiler
range-checking is enabled).
The use
of constant expressions in subrange definitions introduces a syntactic
difficulty. In any type declaration, when the first meaningful character after
= is a left parenthesis, the compiler assumes that an enumerated type is being
defined. Hence the code
const
X = 50;
Y = 10;
type
Scale = (X - Y) * 2..(X + Y) * 2;
produces an error. Work around this problem
by rewriting the type declaration to avoid the leading parenthesis:
type
Scale = 2 * (X - Y)..(X + Y) * 2;
一个子界类型表示相应序数类型(在此叫做基类型base type)中值的子集。任何形如Low..High(这里,Low和High是相同序数类型的常量表达式并且Low小于High)的结构,都表示一个子界类型,该子界包括Low和High之间所有的值。例如,对于已声明的枚举类型
type
TColors = (Red, Blue, Green, Yellow, Orange, Purple, White, Black);
可以定义如下子界类型
type
TMyColors = Green..White;
这里,TMyColors包括Green、Yellow、Orange、Purple和White等值。
还可以用数字和字符(长度是1的串)定义子界类型:
type
SomeNumbers = -128..127;
Caps = 'A'..'Z';
用数字或字符常量定义子界时,基类型是包含指定范围的最小整数类型或字符类型。Low..High结构本身也可以作为类型名称,因此可以直接用于变量声明。例如:
var
SomeNum: 1..500;
声明了一个整数变量,其值的范围是1到500。
子界类型中每个值的序号与其基类型保持一致。(在上面第一个例子中,如果Color是一个值为Green的变量,那么表达式
Ord(Color) 总返回2,而不管变量Color的类型是TColors还是TMyColors。)子界类型的值不会在子界的起点和终点循环,即使基类型是整数或字符类型;对子界类型的边界值递增或递减,只是对其基类型转换相应的值。因此,下面的语句
type
Percentile = 0..99;
var
I: Percentile;
...
I :=
100;
将导致编译错误,而语句
...
I :=
99;
Inc(I);
将把值100赋给变量 I (除非范围检查编译指示有效,即{$R+})。由此也可以看出,子界类型与其基类型的范围一致时,其临界值(最小值和最大值)之间是循环的。
定义子界类型时,常量表达式的使用引出一个造句难点。对于任何类型声明,出现在等号(=)右边第一个有意义的字符如果是圆括号,那么编译器总是假定枚举类型将被定义。因此,
const
X = 50;
Y = 10;
type
Scale = (X - Y) * 2..(X + Y) * 2;
将导致编译错误。要避免此类问题,可以写成如下声明形式(编者注,相信在将来的Object Pascal版本中,这一问题会得到解决或改善):
type
Scale = 2 * (X - Y)..(X + Y) * 2;