Class Helper in Delphi. Is it useful or not ?

Class Helper
Class Helper is a Type that associated with another Class, generally used to extend a class without using inheritance. We can introduce new functionalities with additional methods and properties which can be used with members of associated class. To declare a Class Helper we have to state a Helper name with associated Class name.

The syntax for declaring a class helper is:
type
   TMyClassHelper =  class helper for TMyClass 
     memberList
end;
Example...
// my Class //
type
    TMyClass = class
       procedure MyProc;
       function  MyFunc: Integer;
    end;
     ...
    procedure TMyClass.MyProc;
    var X: Integer;
    begin
       X := MyFunc;
    end;

    function TMyClass.MyFunc: Integer;
    begin
        ...
    end;
  ...
 // Class Helper for TMyClass //
 type
    TMyClassHelper = class helper for TMyClass
      procedure HelloWorld;
      function MyFunc: Integer;
    end;
    ...
     procedure TMyClassHelper.HelloWorld;
    begin
       Writeln(Self.ClassName); // Self refers to TMyClass type, not TMyClassHelper
    end;

    function TMyClassHelper.MyFunc: Integer;
    begin
      ...
    end;
  ...
 // Use of above Class and Class Helper //
 var
   X: TMyClass;
 begin
   X := TMyClass.Create;
   X.MyProc;     // Calls TMyClass.MyProc
   X.HelloWorld; // Calls TMyClassHelper.HelloWorld
   X.MyFunc;     // Calls TMyClassHelper.MyFunc

We can declare a Class Helper to extend TStrings.
Example...
type
  TStringsHelper = class helper for TStrings
  public
    function IsEmpty: Boolean;
  end;
.....
function TStringsHelper.IsEmpty: Boolean;
begin
  Result := Count = 0;
end;  
....
procedure TForm1.Button1Click(Sender: TObject);
begin
  if Memo1.Lines.IsEmpty then
    Button1.Caption := 'Empty'
  else
    Button1.Caption := 'Filled';
end;
   
Class Helper are used extensively in Delphi RTL codes used in SYSTEM.PAS...
Example...
type 
  TSingleHelper = record helper for Single
  private
    function InternalGetBytes(Index: Cardinal): UInt8; inline;
    function InternalGetWords(Index: Cardinal): UInt16; inline;
    procedure InternalSetBytes(Index: Cardinal; const Value: UInt8); inline;
    procedure InternalSetWords(Index: Cardinal; const Value: UInt16); inline;
    function GetBytes(Index: Cardinal): UInt8;
    function GetWords(Index: Cardinal): UInt16;
    function GetExp: UInt64; inline;
    function GetFrac: UInt64; inline;
    function GetSign: Boolean; inline;
    procedure SetBytes(Index: Cardinal; const Value: UInt8);
    procedure SetWords(Index: Cardinal; const Value: UInt16);
    procedure SetExp(NewExp: UInt64);
    procedure SetFrac(NewFrac: UInt64);
    procedure SetSign(NewSign: Boolean);
end;

When we should use Class helper?
We should use Class Helper to extend a class when we cannot (or do not need to) rely on normal class inheritance and interface implementations. For example, if you want extra functionality for an existing instance class and for some reason you are not able to change the existing source. We can create a class helper to add extra functionality. A class helper cannot declare instance data, like new private fields (or properties that would read/write such fields). 
But A class helper can add new methods (function, procedure).

So in general we should avoid extending your own classes. If you need to add some new functionality to your own custom classes then add the new members in the class implementation directly - not using a class helper.

Notes:
Class helpers can be stored in a separate unit, so you can add your own nifty class helpers. Be sure to give these units a easy to remember name like Class Helpers for helpers for the Classes unit.

Class Helper changes in new Delphi versions...
From Delphi XE 3 release you can also extend simple types like integer or string or TDateTime, and have construct 
Example...
var
  s : string;
begin
  s := 'Delphi XE3 helpers';
  s := s.ToUpper;
end;

Comments

Popular posts from this blog

ShellExecute in Delphi

How to send Email in Delphi?

Variants in Delphi. Use of Variant Array. How to check a Variant is unassigned or empty or clear ?