1. Computing

Understanding Delphi Class (and Record) Helpers

What Class/Record Helpers Are? When To Use And When Not To Use!

By

A feature of the Delphi language added some years ago (way back in in Delphi 2005) called "Class Helpers" is designed to let you add new functionality to an existing class (or a record) by introducing new methods to the class (record).

I've already covered class helpers with a few examples where their usage could come handy, like in: TStrings: Implemented Add(Variant) and Extending TWinControl with a ViewOnly property.

This time, you'll see some more ideas for class helpers + learn when to and when not to use class helpers.

Class Helper For...

In simple words, a class helper is a construct that extends a class by introducing new methods in the helper class. A class helper allows you to extend existing class without actually modifying it or inheriting from it.

To extend the VCL's TStrings class you would declare and implement a class helper like the following:


type
TStringsHelper = class helper for TStrings
public
  function Contains(const aString : string) : boolean;
end;
The above class, called "TStringsHelper" is a class helper for the TStrings type. Note that TStrings is defined in the Classes.pas, a unit that is by default available in the uses clause for any Delphi form's unit, for example.

The function we're adding to the TStrings type using our class helper is "Contains". The implementation could look like:


function TStringsHelper.Contains(const aString: string): boolean;
begin
  result := -1 <> IndexOf(aString);
end;
I'm certain you've used the above many times in your code - to check if some TStrings descendant, like TStringList, has some string value in its Items collection.

Note that, for example, the Items property of a TComboBox or a TListBox is of the TStrings type.

Having the TStringsHelper implemented, and a list box on a form (named "ListBox1"), you can now check if some string is a part of the list box Items property by using:


  if ListBox1.Items.Contains('some string') then ...

Class Helpers Go and NoGo

The implementation of class helpers has some positive and some (you might think of) negative impacts to your coding.

In general you should avoid extending your own classes - as if you need to add some new functionality to your own custom classes - add the new stuff in the class implementation directly - not using a class helper.

Class helpers are therefore more designed to extend a class when you cannot (or do not need to) rely on normal class inheritance and interface implementations.

A class helper cannot declare instance data, like new private fields (or properties that would read/write such fields). Adding new class fields is allowed.

A class helper can add new methods (function, procedure).

Before Delphi XE3 you could only extend classes and records - complex types. From Delphi XE 3 release you can also extend simple types like integer or string or TDateTime, and have construct like:

var
  s : string;
begin
  s := 'Delphi XE3 helpers';

  s := s.UpperCase.Reverse;
end;
I'll write about Delphi XE 3 simple type helper in the near future.

Where's MY Class Helper

One limitation to using class helpers that might help you "shoot yourself in the foot" is the fact that you can define and associate multiple helpers with a single type. However, only zero or one helper applies in any specific location in source code. The helper defined in the nearest scope will apply. Class or record helper scope is determined in the normal Delphi fashion (for example, right to left in the unit's uses clause).

What this means is that you might define two TStringsHelper class helpers in two different units but only one will apply when actually used!

If a class helper is not defined in the unit where you use its introduced methods - which in most cases will be so, you do not know what class helper implementation you would actually be using. Two class helpers for TStrings, named differently or residing in different units might have different implementation for the "Contains" method in the above example :(

Use Or Not?

I would say "yes", but be aware of the possible side effects :)

Anyhow, here's another handy extension to the above mentioned TStringsHelper class helper

TStringsHelper = class helper for TStrings
private
  function GetTheObject(const aString: string): TObject;
  procedure SetTheObject(const aString: string; const Value: TObject);
public
  property ObjectFor[const aString : string]: TObject read GetTheObject write SetTheObject;
end;

...

function TStringsHelper.GetTheObject(const aString: string): TObject;
var
  idx : integer;
begin
  result := nil;
  idx := IndexOf(aString);
  if idx > -1 then result := Objects[idx];
end;

procedure TStringsHelper.SetTheObject(const aString: string; const Value: TObject);
var
  idx : integer;
begin
  idx := IndexOf(aString);
  if idx > -1 then Objects[idx] := Value;
end;
I guess you've been adding objects to a string list, and you can guess when to use the above handy helper property.

©2014 About.com. All rights reserved.