Let us consider the following three type declarations:
TProcedure = procedure;
TMethod = procedure of object;
TAnonMethod = reference to procedure;
These are all very similar to each other. In terms of calling instances of each of these three types, the calling code is identical. The differences arise in what can be assigned to variables of these types.
Procedural types
TProcedure
is a procedural type. You can assign to a variable of type TProcedure
something of this form:
procedure MyProcedure;
begin
end;
This is a non object-oriented procedure. You cannot assign an instance or class method to a TProcedure
variable. However, you can assign a static class method to a TProcedure
variable.
Method pointers
TMethod
is a method pointer. This is indicated by the presence of of object
. When you have a variable of type TMethod
you must assign either:
- A instance method of an instantiated object, or
- A class method.
So you can assign either of these:
procedure TMyClass.MyMethod;
begin
end;
class procedure TMyClass.MyClassMethod;
begin
end;
The big difference between a procedural type and a method pointer is that the latter contains a reference to both code and data. A method pointer is often known as a two-pointer procedural type. A variable that contains a method pointer contains references to the code and the instance/class to call it on.
Consider the following code:
var
instance1, instance2: TMyClass;
method1, method2: TMethod;
....
method1 := instance1.MyMethod;
method2 := instance2.MyMethod;
Now, although method1
and method2
refer to the same piece of code, they are associated with different object instances. So, if we call
method1();
method2();
We are invoking MyMethod
on the two distinct instances. That code is equivalent to:
instance1.MyMethod();
instance2.MyMethod();
Anonymous methods
Finally we come to anonymous methods. These are even more general purpose than procedural types and method pointers. You can assign any of the following to a variable defined using the reference to
syntax:
- A plain non object-oriented procedure.
- An instance method of an instantiated class.
- A class method.
- An anonymous method.
For example:
var
AnonMethod: TAnonMethod;
....
AnonMethod := MyProcedure; // item 1 above
AnonMethod := instance1.MyMethod; // item 2
AnonMethod := TMyClass.MyClassMethod; // item 3
Anonymous methods, item 4 above, are those declared in-line in your code. For example:
var
AnonMethod: TAnonMethod;
....
AnonMethod := procedure
begin
DoSomething;
end;
The biggest benefit of anonymous methods when compared to the procedural types and method pointers is that they allow for variable capture. For example consider the following short program to illustrate:
{$APPTYPE CONSOLE}
program VariableCapture;
type
TMyFunc = reference to function(X: Integer): Integer;
function MakeFunc(Y: Integer): TMyFunc;
begin
Result := function(X: Integer): Integer
begin
Result := X*Y;
end;
end;
var
func1, func2: TMyFunc;
begin
func1 := MakeFunc(3);
func2 := MakeFunc(-42);
Writeln(func1(4));
Writeln(func2(2));
Readln;
end.
This has the following output:
12
-84