October 1, 2012

Delphi XE3: Record Helpers for Intrinsic Types

One of the few new language features in Delphi XE3 is the ability to add "methods" to native language types. But this is not a small change for the language!

In terms of language feature, class helpers have been around since Delphi 2005, followed by record helpers. These two constructs let you add to an existing class or record new methods, optionally also replacing existing ones. While this is more of a hack than a regular feature, it becomes important when you want to augment the features of an existing class you don't own, for example a library class. There is a caveat, though: you can have only one class helper active for each class at any time. The last visible class helper from the class scope wins over previous ones.

Fast forward to Delphi XE3. We can now write a helper for native types, and this is likely an effect of Embarcadero embracing a new compiler architecture for mobile support and preparing “future” compatibility. It might also be that Delphi is getting inspired by other languages (mostly dynamic ones, though) that provide this feature.

In any case, you can now write:

type
  TIntHelper = record helper for Integer
    function ToString: string;
  end;

and, given an Integer variable n, call:

n.ToString;

How do you define that pseudo-method and how can it refer to the variable's value? By stretching the meaning of the self keyword to refer to the value the function is applied to:

function TByteHelper.ToString: string;
begin
  Result := IntToStr (self);
end;

Notice that you can apply methods also to constants, like in:

  Caption := 400000.ToString;

However, you cannot do the same for a small value, as the compiler interprets constants of the smaller possible type. So if you want to get out the value 4 as a string you have to use the second form:

  Caption := 4.ToString; // nope!
  Caption := Integer(4).ToString; // ok

Or you can make the first statement to compile by defining a different helper:

type
  TByteHelper = record helper for Byte...

The Delphi XE3 RTL takes advantage of this feature offering a rather complete TStringHelper, with dozens of ready-to-use functions. So that the following code will compile (if you add also the Integer helper above):

var
  s1: string;
begin
  // with a variable
  s1 := 'Hello';
  if s1.Contains('ll') then
    ShowMessage (s1.Substring(3));

  // with a constant
  Left := 'Hello'.Length;

  // chaining
  Caption := ClassName.Length.ToString;
The most relevant application of this new feature in the Delphi XE3 RTL is the TStringHelper I mentioned and used above. You can find some more details at http://theroadtodelphi.wordpress.com/2012/09/05/exploring-delphi-xe3-record-helpers-for-simple-types-system-sysutils-tstringhelper/ (notice, however, that some of record helpers mentioned like the TGUID record helper were already in XE2). I think Nick Hodges also mentioned this and the fact he like the methods chaining. I fully second it, makes code more readable to me, although I know this is debetable and debated. 
Another interesting idea is the open source TDateTime helper offered by Colin on https://github.com/colinj/TDateTimeHelper. If everyone builds his own and given you can have only one helper for each type, we might get into some real fragmentation. So having a few open and community shared helpers is a great idea.

 





 

7 Comments

Delphi XE3 Record Helpers for Intrinsic Types 

Record helpers (esp. for intrinsic types) would be 
soooo much helpful if we could declare multiple helpers 
for one type :(

As it is now, nobody can add useful additions to the 
'string' type because one helper is already defined in 
the RTL.
Comment by gabr [http://thedelphigeek.com] on October 1, 17:21

Delphi XE3 Record Helpers for Intrinsic Types 

Class helpers are attractive, but I don't think they are 
good idea while only one record helper is available at 
the same time.

Embarcadero need to make class creation more flexible 
bringing concepts like partial classes to the delphi 
language. Or at least various helpers at the same time.
Comment by Francisco Ruiz [] on October 1, 17:28

Delphi XE3 Record Helpers for Intrinsic Types 

I find the idea of being able to extend types without 
sub-classing very attractive. Certainly .net extension 
methods have more than proved their worth.

But how can we take seriously a feature that Embarcadero 
have not even documented?
Comment by David Heffernan [] on October 1, 18:36

Delphi XE3 Record Helpers for Intrinsic Types 

I had a lot of discussions with EMBT to add
inheritance for the record helpers (= intrinsic type
helpers).

With XE3 a company has to copy the whole code of the
StringHelper an add its own methods.

Sometimes I do not see the logic behind certain "half
implemented" development steps. 



Comment by Günther Schoch [http://www.gs-soft.com] on October 2, 06:50

What Delphi really needs is a datetime type *not* using a float type 

What Delphi really needs is a datetime type 
implemented without using a float type. It was a bad 
decision made with Delphi 1, and it is time to get 
rid of it. They should really switch to an integer 
type implementation to get rid of float types issues.
Comment by Luigi D. Sandon on October 2, 21:13

Delphi XE3 Record Helpers for Intrinsic Types 

Class helpers are allowed to use inheritance (since XE, 
at least). If the same goes with record helpers, so one 
can extend the existing helpers with other methods and 
functionality. Then, there is no reason anymore to be 
down by the fact we can have a single helper per 
context!
Comment by Alex [] on October 3, 19:46

Delphi XE3 Record Helpers for Intrinsic Types 

When implementing record (and class) helpers for Free
Pascal nearly two years ago I already decided to allow
inheritance for record helpers in non-Delphi modes,
because I saw no reason to disallow them.

When I come around to implement support for type
helpers (I already have a proof of concept
implementation) they'll support inheritance as well of
course.

That said I still plan to add support for "multiple
helpers" though I first need to define when which
method is visible. ;)

Regards,
Sven
Comment by Sven [] on October 5, 11:46


Post Your Comment

Click here for posting your feedback to this blog.

There are currently 0 pending (unapproved) messages.