Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "Reference to" do exactly in Delphi?

While reading the documentation at Anonymous Methods in Delphi I started to wonder. I've always used something like this:

type TMathFn = Function(A, B: Integer): Integer;
var fn: TMathFn;

Always worked for me. But this document tells me to use this instead:

type TMathFn = Reference to Function(A, B: Integer): Integer;
var fn: TMathFn;

And as I've been developing in Delphi from 1994 until 2010 I'm a bit unfamiliar with this "Reference to" part. Still, both options seem to work identically. So...
Are they identical?

like image 897
Wim ten Brink Avatar asked Nov 02 '20 10:11

Wim ten Brink


2 Answers

"REFERENCE TO" is to allow Anonymous Methods (inline definitions of PROCEDUREs/FUNCTIONs), which can Capture a context (for example local variables, which are captured as references, ie. if you change the variable after capture, it's the modified value that is captured, see below).

TYPE TMyProc = REFERENCE TO PROCEDURE(CONST S : STRING);

PROCEDURE Information(CONST S : STRING);
  BEGIN
    MessageDlg(S,mtInformation,[mbOK],0)
  END;

PROCEDURE RunProc(P : TMyProc ; CONST S : STRING);
  BEGIN
    P(S)
  END;

PROCEDURE A(B,C : INTEGER);
  VAR
    D : INTEGER;
    P : TMyProc;

  BEGIN
    D:=3;
    // D is 3 at the time of capture
    P:=PROCEDURE(CONST S : STRING)
         BEGIN
           Information(S+': '+IntToStr(D)+' -> '+IntToStr(B))
         END;
    // D is now 4 - and is reflected in the captured routine, as
    // the capture is done by REFERENCE and not by VALUE.
    INC(D);
    RunProc(P,'Hello')
  END;

BEGIN
  A(2,3)
END.

Will show "Hello: 4 -> 2" in a message box.

The above definition of P "captures" (includes) the variables D and B so that even if you pass it to another function, where these variables don't exist, you can still access them.

This would be (nearly) impossible to do with ordinary PROCEDURE [OF OBJECT] types, as they can't access local variables declared at the point of execution.

like image 181
HeartWare Avatar answered Nov 12 '22 21:11

HeartWare


No, they are not identical.

The difference is that

TMathFn = function(A, B: Integer): Integer;

is an ordinary function,

TMathMethod = function(A, B: Integer): Integer of object;

is a method, and

TMathAnonMethod = reference to function(A, B: Integer): Integer;

is an anonymous method, but you can also assign an ordinary function or a method to a variable of this type.

So, for instance, if

type
  TMathFn = function(A, B: Integer): Integer;
  TMathMethod = function(A, B: Integer): Integer of object;
  TMathAnonMethod = reference to function(A, B: Integer): Integer;

function Test(A, B: Integer): Integer;
begin
  Result := A + B;
end;

type
  TTestClass = class
    function Test(A, B: Integer): Integer;
  end;

{ TTestClass }

function TTestClass.Test(A, B: Integer): Integer;
begin
  Result := A + B;
end;

then the following applies:

procedure TForm1.FormCreate(Sender: TObject);
var
  T: TTestClass;
  F: TMathFn;
  M: TMathMethod;
  AM: TMathAnonMethod;
begin

  T := TTestClass.Create;
  try

    F := Test; // compiles
    F := T.Test; // doesn't compile
    F := function(A, B: Integer): Integer
      begin
        Result := A + B;
      end; // doesn't compile

    M := Test; // doesn't compile
    M := T.Test; // compiles
    M := function(A, B: Integer): Integer
      begin
        Result := A + B;
      end; // doesn't compile

    AM := Test; // compiles
    AM := T.Test; // compiles
    AM := function(A, B: Integer): Integer
      begin
        Result := A + B;
      end; // compiles

  finally
    T.Free;
  end;

end;

Under the hood, as you probably already know, F is simply a (function) pointer and M is a method pointer. Anonymous methods, on the other hand, have a more involved interface-based implementation, which allows all their magic (like variable capture).

like image 41
Andreas Rejbrand Avatar answered Nov 12 '22 20:11

Andreas Rejbrand