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:
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.
type TStringsHelper = class helper for TStrings public function Contains(const aString : string) : boolean; end;
The function we're adding to the TStrings type using our class helper is "Contains". The implementation could look like:
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.
function TStringsHelper.Contains(const aString: string): boolean; begin result := -1 <> IndexOf(aString); end;
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 NoGoThe 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:
I'll write about Delphi XE 3 simple type helper in the near future.
s : string;
s := 'Delphi XE3 helpers';
s := s.UpperCase.Reverse;
Where's MY Class HelperOne 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
I guess you've been adding objects to a string list, and you can guess when to use the above handy helper property.
TStringsHelper = class helper for TStrings
function GetTheObject(const aString: string): TObject;
procedure SetTheObject(const aString: string; const Value: TObject);
property ObjectFor[const aString : string]: TObject read GetTheObject write SetTheObject;
function TStringsHelper.GetTheObject(const aString: string): TObject;
idx : integer;
result := nil;
idx := IndexOf(aString);
if idx > -1 then result := Objects[idx];
procedure TStringsHelper.SetTheObject(const aString: string; const Value: TObject);
idx : integer;
idx := IndexOf(aString);
if idx > -1 then Objects[idx] := Value;