{"id":273,"date":"2008-08-30T13:33:24","date_gmt":"2008-08-30T01:33:24","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=273"},"modified":"2008-08-31T09:37:56","modified_gmt":"2008-08-30T21:37:56","slug":"class-helpers-on-trial","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/273\/","title":{"rendered":"Class Helpers On Trial"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Class helpers (introduced in Delphi <span style=\"text-decoration: line-through;\">2007<\/span> <span style=\"text-decoration: line-through;\">2006<\/span> 2005 &#8211; thanks to Chris and Bruce for the corrections) seem to be cropping up more and more frequently in suggested work-arounds or implementation approaches.\u00a0 I find this worrying given that this language feature has always come with the admonition from CodeGear that it isn&#8217;t advisable to use it!<\/p>\n<p>So why do people seem so keen on using them, and why shouldn&#8217;t they?<\/p>\n<p><!--more--><\/p>\n<p>Class helpers are to be considered &#8220;bad&#8221; when used in an application because they were never designed for general purpose use. As I understand it, they were devised to workaround a problem (or problem<em>s<\/em>) in the VCL that existed for the language (or more accurately, the VCL) implementors, not those who implement code that merely <span style=\"text-decoration: underline;\">uses<\/span> the VCL.<\/p>\n<p>The biggest problem with class helpers, from the point of view of using them in your own applications, is the fact that only <em><strong>one<\/strong><\/em> class helper for a given class may be referenced at any time.<\/p>\n<p>That is, if you have two helpers for a class nominally reachable in your code (e.g. in two separate units both of which are in your uses list), only one of them will actually be &#8220;seen&#8221; by the compiler. You won&#8217;t get any warnings or even hints about any other helpers that exist or may be hidden.<\/p>\n<p>So you can be happily coding away with your lovingly crafted helper, and then you add some other unit to your uses list that unbeknownst to you contains another helper for the same class and suddenly your code simply stops compiling.<\/p>\n<p>Since helpers are used &#8211; as designed &#8211; within the VCL, adding your own helpers for VCL classes is likely to lead to this problem if you also write code that (perhaps unknown to you) relies on the VCL helpers for those classes.<\/p>\n<h3>A Sticky Situation<\/h3>\n<p>You may be able to fudge your way out of this predicament by fiddling with the order of your units in the uses list (once you&#8217;ve figured out what&#8217;s going on, which is likely to come after perhaps your 2nd or 3rd restart of the IDE and an experimental reboot or two before you accept that it isn&#8217;t just the compiler having &#8220;a moment&#8221;).<\/p>\n<p>If you can&#8217;t fix it with the uses list &#8211; if more than 2 helpers are involved &#8211; you might perhaps succeed in moving one or other helper up or down the ancestry of the helped class, assuming you are in control of the source for that helper and hoping that that does not in turn create a conflict with some <span style=\"text-decoration: underline;\">other<\/span> helper out there, perhaps in some other area of your code.<\/p>\n<p>But if you can&#8217;t wriggle your way out of the situation then you are well and truly stuck, because you can&#8217;t even use qualification to forcibly reference the &#8220;hidden&#8221; helper.<\/p>\n<pre class=\"delphi\">  TMyHelper(obj).MyMethod;<\/pre>\n<p>Won&#8217;t compile if <strong>TMyHelper<\/strong> is &#8220;hidden&#8221; by some other helper for the class of <strong>obj<\/strong> involved. In fact, this code won&#8217;t compile anyway because <strong>TMyHelper<\/strong> does not exist as a &#8220;proper&#8221; type, so you can&#8217;t even use this style to aid clarity if you wish (i.e. to make it clear that your code is reliant on a helper).<\/p>\n<h3>But Without Class Helpers, What Can We Do?<\/h3>\n<p>Fortunately there is an alternative, which is not only immune from all these problems but is also in my opinion actually more flexible\/powerful (not least because it works in any Delphi version).<\/p>\n<p>That is, to use explicit helper-<em>style<\/em> pseudo sub-classes, the &#8220;old fashioned&#8221; way, where you adhere to the rules for helpers (only accessing public methods, not adding instance data etc etc) but use explicit hard-casting to &#8220;add&#8221; your methods to an instance.<\/p>\n<pre class=\"delphi\">  TMyHelper = class(TForm)\r\n    procedure MyMethod;\r\n  end;\r\n\r\n  TMyHelper(form).MyMethod;<\/pre>\n<p>This is &#8220;better&#8221; to my mind because:<\/p>\n<p style=\"padding-left: 30px;\">a) it is explicit. No magic. No wondering where these undocumented methods came from (for the uninitiated, unaware of the existence of your &#8220;helper&#8221; class in some obscure unit somewhere).<\/p>\n<p style=\"padding-left: 30px;\">b) it is unbreakable. As long as you stick to the rules for implementing the helper itself, nobody else&#8217;s helper can &#8220;hide&#8221; yours or interfere with how it works.<\/p>\n<p style=\"padding-left: 30px;\">c) it is more powerful and more flexible &#8211; the &#8220;helper&#8221; subclass is able to access <em>protected<\/em> as well as <em>public<\/em> members.<\/p>\n<p>There are three downsides &#8211; two of which are arguably in fact a further benefit, in some situations at least.<\/p>\n<p style=\"padding-left: 30px;\">1) it is necessary to explicitly type-cast in order to invoke your helper functionality.<\/p>\n<p style=\"padding-left: 30px;\">2) the compiler will not prevent you from violating <em>some<\/em> of the rules of class helping (not overriding inherited methods).\u00a0 It still isn&#8217;t possible to access private members, for example.<\/p>\n<p style=\"padding-left: 30px;\">3) protected members are made visible in &#8220;consumer&#8221; code.<\/p>\n<p>It is the first and last of these that I could be considered further <span style=\"text-decoration: underline;\">benefits<\/span>.<\/p>\n<p>In the case of the first, it is made clear that an instance is being treated as something that, strictly speaking, it isn&#8217;t.\u00a0 To my mind the real basis for this complaint is that it involves a little more typing than using a formal helper.<\/p>\n<p>In the case of the third, gaining access to protected members is quite often the very reason that such a class might have been used in the first place, since some aspects of frameworks &#8211; particularly in the case of the VCL &#8211; keep some members protected that would most usefully be made public.<\/p>\n<p>For example, <strong>TControl<\/strong> introduces the <strong>Color<\/strong> property but only as a protected member, so if you have only a <strong>TControl<\/strong> reference to some control whose <strong>Color<\/strong> you wish to change (or inspect) you need some way to bring that protected member into visibility.  You could have a complicated type checking and type casting sequence (hoping you catch all the relevant sub-classes), or you could use a &#8220;helper&#8221;:<\/p>\n<pre class=\"delphi\">  if ctrl is TEdit then\r\n    TEdit(ctrl).Color := aColor\r\n  else if ctrl is TListbox then\r\n    TListbox(ctrl).Color := aColor\r\n  else....<\/pre>\n<p>versus:<\/p>\n<pre class=\"delphi\">  TControlHelper = class(TControl);\r\n\r\n  TControlHelper(ctrl).Color := aColor;<\/pre>\n<p>Note that this is not even possible with a &#8220;proper&#8221; class helper, so if you are using a &#8220;proper&#8221; class helper you will still need this &#8220;dirty&#8221; alternative to solve this particular problem.<\/p>\n<p>Why not simply consistently use one approach that meets all needs?<\/p>\n<h3>But It&#8217;s Not Type Safe!  TWorld.IsEnding!<\/h3>\n<p>Type safety is a potential concern of course, with the risk that you may cast to a helper class inappropriate to the object being cast, but this is not a problem unique to class helpers and you can easily code safely with this technique if you prefer:<\/p>\n<pre class=\"delphi\">  TControlHelper(obj as TControl).Color := aColor;<\/pre>\n<p>Personally I feel the slight risk (which it is in my power to mitigate and avoid) is worth the comfort of knowing that some-one else&#8217;s helper (or perhaps even an ill considered additional helper of my own) is not one day going to break my code.<\/p>\n<h3>So Why Bother WIth Class Helpers At All?<\/h3>\n<p>Good question.   It is rather curious that the VCL problem was not solved using this alternate approach, for example.  Why spend time implementing a seemingly flawed language feature to solve an problem that can be easily solved some other way?<\/p>\n<p>I suspect that class helpers might be earmarked for future things&#8230; they bear an uncanny resemblance to extension methods in C#, for example, which are key to the syntactic candy floss and lexical gymnastics required to deliver LINQ.<\/p>\n<p>But until their purpose is fully realised, I personally think it wise to take heed of CodeGear&#8217;s advice and steer well clear.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>Class helpers (introduced in Delphi 2007 2006 2005 &#8211; thanks to Chris and Bruce for the corrections) seem to be cropping up more and more frequently in suggested work-arounds or implementation approaches.\u00a0 I find this worrying given that this language feature has always come with the admonition from CodeGear that it isn&#8217;t advisable to use [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4],"tags":[52,292,53],"class_list":["post-273","post","type-post","status-publish","format-standard","hentry","category-delphi","tag-class-helpers","tag-delphi","tag-techniques"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-4p","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=273"}],"version-history":[{"count":8,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/273\/revisions"}],"predecessor-version":[{"id":279,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/273\/revisions\/279"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}