Barry Kelly has explained in his recent post how to obtain a method pointer from an anonymous method. Here I will play a little more with the anonymous methods (using Delphi 2009) and consider a somewhat reversed problem – how to create an anonymous method that incapsulates the ordinary function.
First how it is done in the “normal” way:
type
TIntFunc = function(Value: Integer): Integer;
TIntFuncRef = reference to function(Value: Integer): Integer;
function Square(Value: Integer): Integer;
begin
Result:= Value * Value;
end;
function MakeAnonMethod(AFunc: TIntFunc): TIntFuncRef;
begin
Result:= function(Value: Integer): Integer
begin
Result:= AFunc(Value);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AnonMethod: TIntFuncRef;
begin
AnonMethod:= MakeAnonMethod(@Square);
ShowMessage(IntToStr(AnonMethod(2)));
end;
The MakeAnonMethod function creates the anonymous method that captures a function variable (AFunc argument, actually a pointer). That is simple.
Now let us consider what is going on behind the scene. An anonymous method is internally an interface with a single method “Invoke”:
type
TAnonCaller = class(TInterfacedObject, TIntFuncRef)
private
FFunc: TIntFunc;
function Invoke(Value: Integer): Integer;
public
constructor Create(AFunc: TIntFunc);
end;
constructor TAnonCaller.Create(AFunc: TIntFunc);
begin
FFunc:= AFunc;
end;
function TAnonCaller.Invoke(Value: Integer): Integer;
begin
Result:= FFunc(Value);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
AnonMethod: TIntFuncRef;
begin
AnonMethod:= TAnonCaller.Create(@Square);
ShowMessage(IntToStr(AnonMethod(2)));
end;
The second code snippet do the same thing as the first – it “captures” the function variable in TAnonCaller.Create constructor and uses it to call the “captured” function.
The above code snippets show that
- when we create an anonymous method we actually create an object that implements an interface of a special kind and obtain a reference to the interface
- when we call the anonymous method we actually call the Invoke method of the interface.
The above is an oversimplified description of how the anonymous methods work internally. A more closer look at anonymous methods shows that several anonymous methods may share a single object implementing the interfaces for all of them, and that is the way how the different anonymous methods share the captured variables (as fields of the common object).
You don’t need to do that. This line will work just as well:
AnonMethod := Square;
Method references are designed to be assignment compatible on input with methods, functions, method pointers, function pointers and anonymous methods.
Thank you for the remark – nice feature.