Procedural types allow you to
treat procedures and functions as values that can be assigned to variables or
passed to other procedures and functions. For example, suppose you define a
function called Calc that takes two integer parameters and returns an integer:
function
Calc(X,Y: Integer): Integer;
You can assign the Calc
function to the variable F:
var F: function(X,Y:
Integer): Integer;
F := Calc;
If you take any procedure or
function heading and remove the identifier after the word procedure or function,
what’s left is the name of a procedural type. You can use such type names
directly in variable declarations (as in the example above) or to declare new
types:
type
TIntegerFunction = function: Integer;
TProcedure = procedure;
TStrProc = procedure(const S: string);
TMathFunc = function(X: Double): Double;
var
F: TIntegerFunction; {
F is a parameterless function that returns an integer }
Proc: TProcedure; {
Proc is a parameterless procedure }
SP: TStrProc; {
SP is a procedure that takes a string parameter }
M: TMathFunc; {
M is a function that takes a Double (real) parameter
and returns a Double }
procedure
FuncProc(P: TIntegerFunction); { FuncProc
is a procedure whose only parameter
is a parameterless integer-valued
function }
The variables above are all procedure
pointers, that is, pointers to the address of a procedure or function. If
you want to reference a method of an instance object (see Classes and objects), you
need to add the words of object to the procedural type name. For
example
type
TMethod = procedure of object;
TNotifyEvent = procedure(Sender: TObject) of object;
These types represent method
pointers. A method pointer is really a pair of pointers; the first stores
the address of a method, and the second stores a reference to the object the
method belongs to. Given the declarations
type
TNotifyEvent = procedure(Sender: TObject) of object;
TMainForm = class(TForm)
procedure ButtonClick(Sender: TObject);
...
end;
var
MainForm: TMainForm;
OnClick: TNotifyEvent
we could make the following assignment.
OnClick := MainForm.ButtonClick;
Two procedural types are
compatible if they have
· the same
calling convention,
· the same return
value (or no return value), and
· the same number
of parameters, with identically typed parameters in corresponding positions.
(Parameter names do not matter.)
Procedure pointer types are always
incompatible with method pointer types. The value nil can be assigned to
any procedural type.
Nested procedures and functions
(routines declared within other routines) cannot be used as procedural values,
nor can predefined procedures and functions. If you want to use a predefined
routine like Length as a procedural value, write a wrapper for it:
function
FLength(S: string): Integer;
begin
Result := Length(S);
end;
Procedural types in statements and expressions
Data types and variables: Overview
Declaring procedures and functions: Overview
Procedures and functions: Overview
程序型类型允许将过程和函数视为可以赋值到变量或传递给其他过程和函数的值。例如,定义一个叫做Calc的函数,接受两个整数参数并返回一个整数:
function
Calc(X,Y: Integer): Integer;
可以将Calc函数赋值给变量F:
var F: function(X,Y:
Integer): Integer;
F := Calc;
如果把任意过程或函数首部在保留字procedure或function之后的标识符去掉,剩下的就是程序型类型的名称。可以直接用这样的类型名称声明变量(就象上面的例子)或声明新的类型。
type
TIntegerFunction = function: Integer;
TProcedure = procedure;
TStrProc = procedure(const S: string);
TMathFunc = function(X: Double): Double;
var
F: TIntegerFunction; {
F是一个无参数的函数,返回一个整数 }
Proc: TProcedure; {
Proc是一个无参数的过程 }
SP: TStrProc; {
SP是一个过程,接受两个串参数 }
M: TMathFunc; {
M是一个函数,接受两个Double(实数)参数,返回一个Double(实数)值
}
procedure
FuncProc(P: TIntegerFunction); { FuncProc是一个过程,其参数是一个无参数、返回整数值的函数 }
上面所有的变量都是程序指针(procedure
pointers),也就是说,指向过程或函数的地址。如果要引用实例对象(见类和对象)的方法,则要在程序型类型名后面添加保留字of object。例如
type
TMethod = procedure of object;
TNotifyEvent = procedure(Sender: TObject) of object;
这些类型表示的是方法指针(method
pointers)。一个方法指针实际上就是一对指针:第一个指针存储方法地址,第二个指针存储方法所属对象的引用。对如下给出的声明
type
TNotifyEvent = procedure(Sender: TObject) of object;
TMainForm = class(TForm)
procedure ButtonClick(Sender: TObject);
...
end;
var
MainForm: TMainForm;
OnClick: TNotifyEvent
可以有如下赋值语句
OnClick := MainForm.ButtonClick;
两个程序型类型是兼容的,如果它们具有
· 相同的调用约定,
· 相同类型的返回值(或没有返回制),并且
· 相同的参数数量,对应位置的参数具有等同的类型。(参数名可以不一致。)
程序指针类型与方法指针类型总是不兼容的。空指针nil可以赋给任何程序型类型。
嵌套过程和函数(例程声明在其他例程内部)不能用作程序型类型的值,预定义过程和函数也如此。如果要使用预定义例程作为程序型值,可以将其封装在新的例程中,这里以Length为例:
function
FLength(S: string): Integer;
begin
Result := Length(S);
end;