{"id":160,"date":"2008-08-11T23:37:31","date_gmt":"2008-08-11T11:37:31","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=160"},"modified":"2008-08-14T22:21:38","modified_gmt":"2008-08-14T10:21:38","slug":"multicast-events-conclusion","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/160\/","title":{"rendered":"MultiCast Events &#8211; Conclusion"},"content":{"rendered":"<span class=\"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>Drawing this subject to a close (finally!), here&#8217;s the concluding post I promised, including the fully documented and finished implementation that has been serving me well for almost 2 years.\u00a0 The finished implementation incorporates a number of refinements to the core framework, and those are what we shall briefly look at in this final post.<\/p>\n<p><!--more--><\/p>\n<h3>TOnDestroy &#8211; Taking Care of IOn_Destroy<\/h3>\n<p>The <strong>IOn_Destroy<\/strong> mechanism used to manage the relationship between listeners and events is useful, but a little cumbersome.\u00a0 Fortunately, Delphi itself provides a means to greatly simplify the use of this mechanism &#8211; interface delegation.<\/p>\n<p>By implementing a class that takes care of the <strong>IOn_Destroy<\/strong> interface, we can add <strong>IOn_Destroy<\/strong> support to any class much more simply as shown below, using a form class as an example:<\/p>\n<pre class=\"delphi\">    TMainForm = class(TForm, IOn_Destroy)\r\n      \/\/ Published declarations\r\n    private\r\n      fOn_Destroy: IOn_Destroy;\r\n    public\r\n      constructor Create(Owner: TComponent); override;\r\n      property On_Destroy: IOn_Destroy read fOn_Destroy implements IOn_Destroy;\r\n    end;\r\n\r\nconstructor TMainForm.Create(Owner: TComponent); override;\r\nbegin\r\n  inherited;\r\n  fOn_Destroy := TOnDestroy.Create(self);\r\nend;<\/pre>\n<p>That&#8217;s it &#8211; done.<\/p>\n<p>There are a couple of things to note:<\/p>\n<p>Firstly, the private member declaration <strong>fOn_Destroy<\/strong> holds an <span style=\"text-decoration: underline;\">interface<\/span> reference, NOT an object reference.  This is essential for one reason, and useful for another.<\/p>\n<p>It is essential because otherwise the reference counting system in Delphi will cause our aggregated object to be prematurely destroyed (the multicast events framework does not hold on to any references to <strong>IOn_Destroy<\/strong> &#8211; references are obtained, used to access the exposed <strong>On_Destroy<\/strong> event, and then discarded).<\/p>\n<p>It is useful because &#8211; unless you are doing something beyond the scope of this series of posts &#8211; this will be the ONLY reference kept to the class implementing <strong>On_Destroy<\/strong> for our class.\u00a0 So when any <strong>TMainForm<\/strong> instance is destroyed, this sole reference to it&#8217;s <strong>IOn_Destroy<\/strong> implementation is released, automatically freeing the object providing that implementation.\u00a0 In otherwords, we don&#8217;t have to worry about remembering to free the aggregated object.<\/p>\n<p>This comes with a small price though.\u00a0 When using interface delegation to add <strong>IOn_Destroy<\/strong> support to a class the previous specification for the <strong>IOn_Destroy<\/strong> interface makes it a little cumbersome to work with, requiring code that accesses the <strong>On_Destroy<\/strong> event to go through some awkward indirection:<\/p>\n<pre class=\"delphi\">  var\r\n    mainform: TMainForm;\r\n\r\n  mainform.On_Destroy.On_Destroy.Add( handler );<\/pre>\n<p>To alleviate this, the original <strong>IOn_Destroy<\/strong> interface is renamed <strong>IOn_Destroy_<\/strong> and a new <strong>IOn_Destroy <\/strong>interface introduced, extending the original:<\/p>\n<pre class=\"delphi\">  IOn_Destroy_ = interface\r\n    function get_On_Destroy: TMultiCastNotify;\r\n    property On_Destroy: TMultiCastNotify read get_On_Destroy;\r\n  end;\r\n\r\n  IOn_Destroy = interface(IOn_Destroy_)\r\n    procedure Add(const aHandler: TNotifyEvent);\r\n    procedure Remove(const aHandler: TNotifyEvent);\r\n  end;<\/pre>\n<p>The <strong>TMultiCastEvent<\/strong> implementation is modified to use the base interface, so if you have a class with an existing <strong>On_Destroy<\/strong> event, you can still expose that to the framework by directly implementing <strong>IOn_Destroy_<\/strong> if you wish and the mechanism continues to work as before.<\/p>\n<p>However, if you use the <strong>TOnDestroy<\/strong> class to add <strong>On_Destroy<\/strong> support, you have the choice of exposing it either as <strong>IOn_Destroy_<\/strong> <em>or<\/em> the more user-friendly <strong>IOn_Destroy<\/strong>.\u00a0 The <em>Add()<\/em> and <em>Remove()<\/em> methods exposed by this interface allow your code to use the exposed <strong>IOn_Destroy<\/strong> as if it were the <strong>On_Destroy<\/strong> event itself:<\/p>\n<pre class=\"delphi\">  var\r\n    mainform: TMainForm;\r\n\r\n  mainform.On_Destroy.Add( handler );<\/pre>\n<h3>Destruction &#8211; Only Half The Story<\/h3>\n<p>So implementing <strong>On_Destroy<\/strong> support is made a great deal less tiresome.<\/p>\n<p>But adding support for a number of events is still a pretty long-winded affair.\u00a0 Imagine some class providing multicast events for create, change and destroy events:<\/p>\n<pre class=\"delphi\">  fOn_Create := TMultiCastNotify.Create(self);\r\n  fOn_Change := TMultiCastNotify.Create(self);\r\n  fOn_Destroy := TMultiCastNotify.Create(self);<\/pre>\n<p>Surely there is something we can do about this?<\/p>\n<p>Fortunately, yes. <em> Un<\/em>fortunately, it&#8217;s not something that we can handle generically in the base implementation.\u00a0 We can however introduce some assistance in the <strong>TMultiCastNotify<\/strong> event class, along with another useful helper routine.  So let&#8217;s take a look at the additions to the final declaration for the <strong>TMultiCastNotify<\/strong> event class:<\/p>\n<pre class=\"delphi\">    TMultiCastNotify = class(TMultiCastEvent);\r\n      :\r\n      class procedure CreateEvents(const aSender: TObject;\r\n                                   const aEvents: array of PMultiCastNotify);\r\n      procedure DoEventFor(const aSender: TObject);\r\n      :\r\n    end;<\/pre>\n<p><strong>CreateEvents<\/strong> is a basic factory method &#8211; it will instantiate a new instance of <strong>TMultiCastNotify<\/strong> for each reference passed to it.\u00a0 The references have to be passed by address, but that is only a slight inconvenience, compared to the alternative:<\/p>\n<pre class=\"delphi\">  TMultiCastNotify.CreateEvents(self, [@fOn_Create,\r\n                                       @fOn_Change,\r\n                                       @fOn_Destroy]);<\/pre>\n<p>There is a slight risk introduced by such a method &#8211; what if we passed the same reference more than once, or a reference that has already been assigned?<\/p>\n<p>This we <em><strong>can<\/strong><\/em> take care of in the base class.<\/p>\n<p><strong>TMultiCastEvent<\/strong> now supports a <em>CheckReferences()<\/em> method.\u00a0 This method inspects a supplied array of pointers to methods (actually pointers to pointers to methods, to be precise).\u00a0 It inspects each method reference and checks for two things:<\/p>\n<p style=\"padding-left: 30px;\">1. that each reference is unique<\/p>\n<p style=\"padding-left: 30px;\">2. that each reference is currently unassigned<\/p>\n<p>The implementation of the <em>CreateEvents()<\/em> method includes an initial call to <em>CheckReferences()<\/em> to ensure we don&#8217;t make these mistakes when using <strong>TMultiCastNotify<\/strong>, and as a result, both of these example will result in a runtime error (EInvalidPointer exception):<\/p>\n<pre class=\"delphi\">    \/\/ Reference already assigned - this will fail...\r\n    fOn_Create := TMultiCastNotify.Create(self);\r\n   \u00a0TMultiCastNotify.Create(self, [@fOn_Create]);\r\n\r\n    \/\/ Duplicate references - this will fail...\r\n    fOn_Create := NIL;\r\n    TMultiCastNotify.Create(self, [@fOn_Create, @fOn_Create]);<\/pre>\n<p>Since <em>CheckReferences()<\/em> is little more than a more-sophisticated-than-usual <em>ASSERT()<\/em>, it is subject to an <strong>{$ifopt C+}<\/strong> conditional compilation directive.  i.e. it is only part of the class if compiling with ASSERTs turned on.  If you implement your own multicast event classes with similar factory methods you will need to respect this compiler setting, just as <strong>TMultiCastNotify<\/strong> does.<\/p>\n<h3>Do Me A Favor<\/h3>\n<p>The last small addition to <strong>TMultiCastNotify<\/strong> that you may have noticed is the addition of a <em>DoEventFor()<\/em> method.<\/p>\n<p>Just as with the unicast form of <strong>TNotifyEvent<\/strong>, occasionally we need to fire an event and have that event represent a different sender from the usual.\u00a0 <em>DoEventFor()<\/em> simply satisfies this occasional need on our multicast event.<\/p>\n<h3>TMultiCastEvent vs The Alternatives<\/h3>\n<p>A number of attempts have been made to implement, or at least approximate, multicast events in Delphi over the years &#8211; more so I think since C# and .NET arrived on the scene.<\/p>\n<p>Most of those that I have come across either make no attempt to provide a framework for implementing event types not supported &#8220;out of the box&#8221;, other than the &#8220;copy+paste+modify&#8221; reuse paradigm, or place rather obscure demands on implementers of events, and\/or consumers of those events.<\/p>\n<p>The overriding aim in my implementation was to make working with multicast events as close as possible to working with existing Delphi events.\u00a0 100% compatability of existing handlers with multicast events is, I think, a pretty good achievement.<\/p>\n<p>\ud83d\ude42<\/p>\n<h3>MultiCast Events In The Future<\/h3>\n<p>Mention has recently been made of possible work in this area in future versions of Delphi.<\/p>\n<p>Whatever comes out of that exercise I for one hope that we don&#8217;t end up with a solution that mimics C#\/.NET events.\u00a0 It&#8217;s an approach that I don&#8217;t think &#8220;fits&#8221; Delphi at all.<\/p>\n<p>If nothing else, having had multicast events available to me for the past 2 years, one thing I have learned is that not all events need to be multicast.\u00a0 This is most especially true in the GUI.\u00a0 Multicast events have proven to me to be most useful in business, and framework, objects.\u00a0 (My examples have used GUI controls simply because they provide a convenient and visual test bed for demonstrations).<\/p>\n<p>As promised at the outset, the fully documented implementation is now available for download.\u00a0 This final version includes a help file generated from the source (plus some additional topic content) using the excellent <a href=\"http:\/\/www.toolsfactory.com\/\" target=\"_blank\">Doc-o-Matic<\/a>.<\/p>\n<p>[dm]3[\/dm]<\/p>\n<p><em><strong>UPDATE: <\/strong>An updated version with bug fix and minor enhancements is now available.\u00a0 Full details of changes in this update are <a href=\"https:\/\/www.deltics.co.nz\/blog\/?p=189\" target=\"_self\">available here<\/a>.<br \/>\n<\/em><\/p>\n<p>[dm]7[\/dm]<\/p>\n<p>The download contains a help file (Win32 Help format) and two versions of the unit.  One named MultiCast.pas, the other Deltics.MultiCast.pas.  That&#8217;s the only difference.  I primarily use Delphi 7 and so my library of some 200+ units are named with this dot-notation, but the code should be usable in earlier versions of Delphi where this dot-notation is not supported, so this renaming task has been taken care of for the convenience of users of those earlier versions.<\/p>\n<p>\ud83d\ude42<\/p>\n<p>I should confess that, as I alluded to in a couple of the posts in this series, the process of blogging about this work gave me some fresh insights into what I had thought was a very well settled implementation.  As a result the finished unit provided here is slightly modified from the version I&#8217;ve been using.\u00a0 The changes are neither extensive nor significant, but still, in the interest of full disclosure&#8230;.<\/p>\n<p>If anyone has any problems (or for that matter, further suggestions or even just comments of any sort) about the implementation, please do not hesitate to <a href=\"mailto:jsmith@deltics.co.nz\">get in touch<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"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> Drawing this subject to a close (finally!), here&#8217;s the concluding post I promised, including the fully documented and finished implementation that has been serving me well for almost 2 years.\u00a0 The finished implementation incorporates a number of refinements to the core framework, and those are what we shall briefly look at in this final post.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[292,28,33,25,32],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-2A","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":86,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/86\/","url_meta":{"origin":160,"position":0},"title":"Multicast Events &#8211; Part 1","date":"05 Aug 2008","format":false,"excerpt":"I remember one of the things that got me excited when I first read about the as-then new fangled C# and .NET stuff coming out of Microsoft was the idea that a single event could have multiple handlers. Cutting a long and irrelevant story short, my interest in .NET waned,\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":137,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/137\/","url_meta":{"origin":160,"position":1},"title":"Multicast Events &#8211; Part 3","date":"09 Aug 2008","format":false,"excerpt":"So far we've seen a multicast event implementation in (fairly limited) action, and dissected the core of it's implementation, which was a fairly dry affair. I also demonstrated a flaw in the initial implementation - a susceptibility to objects adding handlers to events but not removing them when being destroyed.\u00a0\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":102,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/102\/","url_meta":{"origin":160,"position":2},"title":"Multicast Events &#8211; Part 2","date":"07 Aug 2008","format":false,"excerpt":"Having covered some of the basic use of multicast events, in this second post I shall start to build the implementation.\u00a0 In this first iteration we will provide the basics of a multicast event - managing and calling multiple handlers and the ability to enable and disable an event. The\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":180,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/180\/","url_meta":{"origin":160,"position":3},"title":"Latest Tibur\u00f3n Preview","date":"14 Aug 2008","format":false,"excerpt":"I was a little disappointed that the preview webinar this morning was little more than a re-run of the same content from a little over a week ago, albeit with some downloadable PowerPoint slides this time. It was at least an opportunity for some more Q&A and a couple of\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":112,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/112\/","url_meta":{"origin":160,"position":4},"title":"Multicast Events &#8211; Video Demo 1","date":"07 Aug 2008","format":false,"excerpt":"My second post on multicast events is now up, and here's a video showing the basics. It was also an excuse to get to grips with the video capturing software - CamStudio - (and technique!), which proved to be a frustrating exercise to say the least, but I am quite\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":147,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/147\/","url_meta":{"origin":160,"position":5},"title":"MultiCast Events &#8211; Video #2","date":"09 Aug 2008","format":false,"excerpt":"At the end of my last post I described a problem that arises if an object adds handlers to an event and is then destroyed without having removed those handlers.\u00a0 This video demonstrates that problem and also shows a solution provided by the multicast events framework itself. The details of\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/160"}],"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=160"}],"version-history":[{"count":19,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/160\/revisions"}],"predecessor-version":[{"id":1736,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/160\/revisions\/1736"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=160"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=160"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=160"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}