Anonymous code
Anonymous code is all about treating code as data. You can assign an implementation of a function or procedure to a variable; you pass functions as parameters to other functions and receive them as results. This is a very powerful feature of the Object Pascal language. With anonymous code, the source code of your app can be more compact and maintainable.
A declaration of an anonymous method type has the following syntax:
type TStringProc = reference to procedure (s: string);
We are saying that TStringProc is a procedure that takes a string parameter. Now, we can define variables of this string type, assign implementation to them, pass them to functions, or, simply, just call them. Refer to the following code snippet:
procedure CallMe(const proc: TStringProc; msg: string); begin proc(msg); end; procedure DoStringProc; var proc: TStringProc; begin proc := procedure(x: string) begin Log('Declared proc got: ' + x); end; CallMe(proc, 'Hello'); CallMe( procedure(v: string) begin Log('Inline code got: ' + v); end, 'World'); end;
Here, we have a CallMe procedure that takes as a parameter a chunk of code compatible with TStringProc and a string variable. There is also a proc local variable of the TStringProc type. In the first lines of the DoStringProc routine, we assign implementation to the proc variable. Note that there is no identifier after the procedure keyword. The parameter declaration follows immediately. This is why it is called anonymous code. It does not need a name because it is never referenced by name. In the first call to CallMe, we pass the proc variable as a parameter. The second call to CallMe is even more compact; we define the implementation of an anonymous procedure, in-place.
A more useful example of using anonymous code could be the sorting of a generic list. We can define the TPersonList class and implement the SortByFullName method:
unit uPersonList; interface uses System.Generics.Collections, // TObjectList<T> uPerson; // TPerson type TPersonList = class(TObjectList<TPerson>) procedure SortByFullName; end; implementation uses System.Generics.Defaults, // IComparer, TComparison System.SysUtils; // CompareStr { TPersonList } procedure TPersonList.SortByFullName; var Comparer : IComparer<TPerson>; Comparison : TComparison<TPerson>; begin Comparison := function(const p1, p2: TPerson): integer begin Result := CompareStr(p1.FullName, p2.FullName); end; Comparer := TComparer<TPerson>.Construct(Comparison); inherited Sort(Comparer); end; end.
The generic object list class defines the Sort method, which can be overridden in the descendent classes. As its parameter, it expects a generic comparer class, which is only responsible for comparing any two elements of the underlying class; it returns an integer value that needs to be negative if the first element is less than the second, "zero" if they are the same, or a positive value if the first element is greater than the second one. Here, we construct the TComparer class, which is responsible for comparting to TPerson objects. Luckily in the System.SysUtils unit, there is a very handy CompareStr function that we can use directly. Our comparer is referenced by its implementing interface, and that's why we do not need to free it afterwards. It will be freed automatically by the interface reference counting mechanism.