Record Helpers for Intrinsic Types

I guess that most of you already heart about it. With Delphi XE3 EMBT introduced record helpers for intrinsic types. E.g. Marco Cantu and Nick Hodges already blogged about it.

Just to explain what record helpers for intrinsic types are I will show an example.

program TestHelper;

{$APPTYPE CONSOLE}

{$R *.res}

type
  TMyHelper = record helper for string
  public
    function MyFunction: Integer;
  end;

{ TMyHelper }

function TMyHelper.MyFunction: Integer;
begin
  Result := 42;
end;

var
  sBuffer: string;

begin
  Writeln(sBuffer.MyFunction);
  Readln;
end.

As you can see the record helper allows you to write code that looks like calling a method of an intrinsic type instead of calling a function with the intrinsic type as an argument. This strategy is more object oriented and beautifies your code.

In Delphi XE3 EMBT already introduced some beautiful record helpers. The record helper for the string is the most important and powerful one. It has some really useful functions like Length, Equals, Format, IndexOf etc.. You can find it in the unit System.SysUtils.

Certainly you can write your own record helper for different types. You can also write a record helper for a string and write your own methods.

In the case you add your own helper unit to your uses clause and also System.SysUtils, the compiler only sees one record helper. The record helper which you add at last to your uses clause will win. Let’s have a look at an example:

unit MyHelper;

interface

type
  TMyHelper = record helper for string
  public
    function MyFunction: Integer;
  end;

implementation

{ TMyHelper }

function TMyHelper.MyFunction: Integer;
begin
  Result := 42;
end;

end.

With this unit you can replace the original string helper from System.SysUtils:

program TestHelper;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  MyHelper in 'MyHelper.pas';

var
  sBuffer: string;

begin
  Writeln(sBuffer.Length); //This line doesn't work any longer
  Writeln(sBuffer.MyFunction);
  Readln;
end.

Instead of replacing the original helper it would be nice to derive from it but this is simply not possible. This code doesn’t compile.

type
  TMyHelper = record helper(TStringHelper) for string
  end;

Although class helpers can be derived it is not possible for record helpers. I guess it is because records also cannot be derived.

This issue means that it is nearly impossible to extend the original record helpers. IMO it must be fixed with the next Delphi version.

Anyway, currently I can imagine only one strategy to extend the original record helpers. You have to write you own helper, you have to add all functions of the original one and then you have to call the original function inside your function. And don’t forget to declare them inline because of performance. Since your helper replaces the original one you have to write a call unit for the original helper.

Let’s have a look at an example. First write the caller unit:

unit MyStringCaller;

interface

type
  TMyStringCaller = record
  public
    class function ToUpper(const S: string): string; static; inline;
  end;

implementation

uses
  System.SysUtils;

{ TMyStringCaller }

class function TMyStringCaller.ToUpper(const S: string): string;
begin
  Result := S.ToUpper;
end;

end.

And then your own record helper:

unit MyHelper;

interface

uses
  System.SysUtils, MyStringCaller;

type
  TMyHelper = record helper for string
  public
    function Reverse: string;
    function ToUpper: string; inline;
  end;

implementation

uses
  System.StrUtils;

{ TMyHelper }

function TMyHelper.Reverse: string;
begin
  Result := ReverseString(Self);
end;

function TMyHelper.ToUpper: string;
begin
  Result := TMyStringCaller.ToUpper(Self);
end;

end.

Last but not least you can use your record helper:

program TestHelper;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  MyHelper in 'MyHelper.pas';

var
  sBuffer: string;

begin
  sBuffer := 'Hallo World!';
  Writeln(sBuffer.Reverse);
  Readln;
end.
This entry was posted in Tips and Tricks and tagged . Bookmark the permalink.