An
identifier, such as a variable or function name, can be used only within the scope
of its declaration. The location of a declaration determines its scope. An
identifier declared within the declaration of a program, function, or procedure
has a scope limited to the block in which it is declared. An identifier
declared in the interface section of a unit has a scope that includes any other
units or programs that use the unit where the declaration occurs. Identifiers
with narrower scope especially identifiers declared in functions and procedures
are sometimes called local, while identifiers with wider scope are
called global.
The rules
that determine identifier scope are summarized below.
|
If the identifier is declared in ... |
its scope extends ... |
|
the declaration of a program, function, or procedure |
from the point where it is declared to the end of the current block, including all blocks enclosed within that scope. |
|
the interface section of a unit |
from the point where it is declared to the end of the unit, and to any other unit or program that uses that unit. (See Programs and units.) |
|
the implementation section of a unit, but not within the block of any function or procedure |
from the point where it is declared to the end of the unit. The identifier is available to any function or procedure in the unit, including the initialization and finalization sections, if present. |
|
the definition of a record type (that is, the identifier is the name of a field in the record) |
from the point of its declaration to the end of the record-type definition. (See Records.) |
|
the definition of a class (that is, the identifier is the name of a data field property or method in the class) |
from the point of its declaration to the end of the class-type definition, and also includes descendants of the class and the blocks of all methods in the class and its descendants. (See Classes and objects.) |
When one
block encloses another, the former is called the outer block and the
latter the inner block. If an identifier declared in an outer block is
redeclared in an inner block, the inner declaration overrides the outer one and
determines the meaning of the identifier for the duration of the inner block.
For example, if you declare a variable called MaxValue in the interface
section of a unit, and then declare another variable with the same name in a
function declaration within that unit, any unqualified occurrences of MaxValue
in the function block are governed by the second, local declaration. Similarly,
a function declared within another function creates a new, inner scope in which
identifiers used by the outer function can be redeclared locally.
The use
of multiple units further complicates the definition of scope. Each unit listed
in a uses clause imposes a new scope that encloses the remaining units
used and the program or unit containing the uses clause. The first unit
in a uses clause represents the outermost scope and each succeeding unit
represents a new scope inside the previous one. If two or more units declare
the same identifier in their interface sections, an unqualified reference to
the identifier selects the declaration in the innermost scope, that is, in the
unit where the reference itself occurs, or, if that unit doesn’t declare the
identifier, in the last unit in the uses clause that does declare the
identifier.
The System
unit is used automatically by every program or unit. The declarations in System,
along with the predefined types, routines, and constants that the compiler
understands automatically, always have the outermost scope.
You can override these rules of scope and by-pass an inner declaration by using a qualified identifier (see Qualified identifiers) or a with statement (see With statements).
一个标识符,例如一个变量或函数名,可以在其相应声明的作用域(scope)中被使用。声明的位置决定了标识符的作用域。在程序、函数或过程的声明中声明的标识符,其作用域仅限于其声明所在的块。在单元接口节中声明的标识符,其作用域还包括该单元在其客户(使用了该单元的单元或程序)中出现的位置。在函数和过程中声明的标识符,其作用域较小,有时叫做局部的(local),如局部变量、局部常量等,而将作用域相对较广的标识符叫做全局的(global),如全局变量,全局常量等。
标识符作用域规则可以概括为:
|
如果标识符声明在... |
那么其作用域的范围是... |
|
程序、函数或过程的声明中 |
自声明处到当前块结束,包括封装在当前块中的块(子块) |
|
单元的接口节中 |
自声明处到单元结束,以及任何使用该单元的单元和程序(见 程序和单元。) |
|
单元的实现节中,但不在任何函数或过程中 |
自声明处到单元结束。标识符对于单元中的任何函数和过程都是可用的,包括初始化节和结束节(如果有) |
|
记录类型的定义中(即标识符是记录的字段名) |
自声明处到记录类型的定义结束(见记录) |
|
类的定义中(即标识符作为类的属性、方法等的名称) |
自声明处到类类型定义结束,并且还包括类的后裔、类极其后裔类的所有方法的块(类和对象)。 |
当一个块封装了另一个块时,前者叫做外部块,后者叫做内部块。如果声明在外部块中的标识符又在内部块中重新声明(同名),那么在整个内部块中,内部块声明的标识符将忽略外部块中的声明(也就是说,编译器在整个内部块中都将该标识符作为内部块的声明解释和使用)。例如,如果在单元的接口节声明了变量MaxValue,然后在单元中的某个函数中声明了同名变量,并且在函数块中引用时没有冠以任何限定词,那么函数块中对MaxValue的引用将解释为局部变量,即函数中的声明。同样,函数中如果创建了另一个函数,在外部作用域中使用的标识符,在内部域中也可以重新声明同名的局部变量(见当前主题编者注中的过程和函数的嵌套定义)。
多重单元引用使作用域的定义变得更为复杂。列在uses子句中的每个单元都强制增加了一个新的作用域,该作用域封装了被使用的其他单元、程序以及包含在该单元uses子句中的单元。uses子句中的第一个单元表示了最外层的作用域,紧接其后的单元表示了在该作用域内的新的作用域。如果在两个或更多单元的接口节中声明了相同标识符,那么当引用该标识符而没有冠以限定词时,将选择最内层作用域中的声明,也就是说,选择引用该标识符的单元,或者,如果该引用该标识符的单元中没有对其声明,那么将选择在单元uses子句的单元列表中最后一个声明了该标识符的单元。(见当前主题编者注中的接口节中uses子句对作用域的影响和接口节和实现节中uses子句对作用域的影响)
Borland提供的System单元自动被每个程序或单元使用。System单元中的所有声明和编译器能自动识别的预定义类型、例程、常量等一起,总是作为最外层的作用域。(见当前主题编者注中的唯一特殊的System单元)
可以通过使用冠以限定词的标识符(见限制标识符)或with语句(见With语句)来超越上述作用域规则从而绕过内层作用域的声明。
过程和函数的嵌套定义,只在很少的时候使用,一般为了增强代码的可读性而采用。例如:
function
DoTest: Boolean;
var
N: Integer;
...
procedure DoOnce;
var
J: string;
begin
... { 在过程DoOnce可以引用函数DoTest中声明的变量N }
end;
...
begin
for I := A to B do
begin
DoOnce;
... { 在函数DoTest中不能使用内部过程DoTest中的局部变量J }
end;
end;
不难看出,这里的结构和程序(program)的结构是类似的。
接口节中uses子句对作用域的影响
例如,假定单元Unit1、Unit2、Unit3的接口节中都声明了变量X,而在单元UnitA中没有声明同名的全局变量X,那么,对于单元UnitA,如果其uses子句写成uses Unit1, Unit2, Unit3; 并且引用变量X时没有冠以单元名作为限定词时,那么编译器将把X解释成为Unit3.X,因为此时uses子句中由于引用Unit3而增加的作用域是最内层的。)
接口节和实现节中的uses子句对作用域的影响
对于单元UnitA,假定其接口节中有uses子句
uses
Unit1, Unit2;
实现节中有uses子句
uses
Unit3, Unit4;
再假定单元Unit1、Unit2、Unit3、Unit4的接口节中都声明了常量MaxValue,而在单元UnitA中没有声明同名的全局常量MaxValue。
那么,在单元UnitA的接口节中,对常量MaxValue的引用(不冠以限定词,以下同)将解释成为Unit2.MaxValue;而在单元UnitA的实现节中,对常量MaxValue的引用将解释成为Unit4.MaxValue。
也就是说,对于单元UnitA的实现节,实现节中uses子句单元对应的作用域要比接口节中uses子句单元对应的作用域范围窄,应视为内层;对于单元UnitA的接口节,实现节中的uses子句列出的单元中向外提供的所有公共实体都是不可用的,因此也就与作用域无关。
根据当前主题编者注中的接口节和实现节中uses子句对作用域的影响介绍的范例,可以看出,无论将System单元显式地放置在接口节中还是实现节中uses子句中,也无论是放置在uses子句中的什么位置,都无法根据作用域规则将其总是作为最外层的作用域。因此,System单元只能作为唯一特殊的单元,隐式地被使用,并超越规则而总是作为最外层的作用域。