Wednesday, March 11, 2020

Understanding Delphi Class (and Record) Helpers

Understanding Delphi Class (and Record) Helpers 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). Below youll 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 VCLs 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 forms unit, for example. The function were 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; Im certain youve 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; Ill write about Delphi XE 3 simple type helper in the near future. Wheres 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 units 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? Yes, but be aware of the possible side effects. Heres 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; If youve been adding objects to a string list, you can guess when to use the above handy helper property.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.