tag:blogger.com,1999:blog-85946905509751357982024-02-20T00:36:51.683-08:00Kelly Leahy (technical)Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.comBlogger44125tag:blogger.com,1999:blog-8594690550975135798.post-71010518264175080812016-04-21T23:43:00.004-07:002016-04-21T23:43:49.049-07:00No Git-Gui, I don't ever want you to compact my database. Kthxbye.So... for years now I've been hacking git-gui to turn off the GC check since it really annoyed me and until today didn't know how to turn it off "properly".<br />
<br />
Today, I learned you are just supposed to set a config option to turn it off (go figure - I don't know if this is new or if I just never looked hard enough).<br />
<br />
Anyway, here's the info in case you want it.<br />
<br />
Just set the option <span style="font-family: Courier New, Courier, monospace;">gui.gcwarning</span> to <span style="font-family: Courier New, Courier, monospace;">false</span><span style="font-family: inherit;">.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Do it like this:</span><br />
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">git config --global gui.gcwarning false</span></blockquote>
<span style="font-family: inherit;">If you're interested how I was doing it before (you really shouldn't be but in case you're curious) - under your git install folder there's a file somewhere (probably called </span><span style="font-family: Courier New, Courier, monospace;">git-gui.tcl</span><span style="font-family: inherit;">) that makes a call to a function called </span><span style="font-family: Courier New, Courier, monospace;">hint_gc</span><span style="font-family: inherit;"> that is the function that shows the annoying message. I just commented out that line in previous builds. Now, I see that it checks a config variable, but I guess I either didn't notice or it didn't used to check.</span>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-62264072130862025112015-12-17T11:37:00.003-08:002015-12-17T11:41:36.901-08:00ATL OLE DB Provider Wizard IssuesThere are some issues with the example ATL OLE DB Provider Wizard generated code that cost me some time when playing around with it today.<br />
<br />
I thought I'd post my solutions in case it helps others.<br />
<br />
<ol>
<li>The generated code does not use the COM_INTERFACE_ENTRY2 macro properly - instead of the line of code on which you'll receive a compile error, you should have: COM_INTERFACE_ENTRY2(ICommand, ICommandText) -- notice, this is a reversal of the parameters. Apparently, the ATL macro changed the order of the arguments at some point in the past (or the sample code is just wrong). In any case, the base class goes on the left and the derived class on the right (not the way it was in the sample).</li>
<li>The generated code includes PROVIDER_COLUMN_ENTRY_STR(...) for the column map in the C...WindowsFile class that is auto-generated in the ...RS.h file. If your project is compiled for UNICODE (as mine was), you need to make this PROVIDER_COLUMN_ENTRY_WSTR instead. You'll see the issue when you see only a single character for each of the file names (rather than the full file name) when using the provider.</li>
</ol>
Anyway, hope this helps anyone who runs into these issues.Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-5765646745193109072011-08-24T19:58:00.000-07:002011-08-24T20:48:20.756-07:00Azure Fast-Deploy<div>We have been building a web system on Windows Azure for quite a while now in my company, and are very familiar with the 20 minute development cycle of redeploying an Azure Role. Recently, the Windows Azure team released a streamlining process for doing web deployments to Azure (for development purposes only, not production deployments). Andy Cross talks about it here: <a href="http://blog.bareweb.eu/2011/04/using-web-deploy-in-azure-sdk-1-4-1/">http://blog.bareweb.eu/2011/04/using-web-deploy-in-azure-sdk-1-4-1/</a></div><div>
<br /></div><div> </div><div>The azure team blog talks about it <a href="http://blogs.msdn.com/b/windowsazure/archive/2011/04/15/now-available-windows-azure-sdk-1-4-refresh-with-webdeploy-integration.aspx">here</a>.</div><div> </div><div>
<br /></div><div>We tried to use Web Deploy, but we don't do things from Visual Studio. We instead do everything from the command line for our automation (since we do a TON of automation). As such, we tried for a while (to no avail) to get Web Deploy to work from the command line (tried for a while for us is measured in minutes not days, we don't have much patience for crappy tooling on our team).</div><div> </div><div>
<br /></div><div>So... we built our own.</div><div> </div><div>
<br /></div><div>It works like this - we have a very simple console app that we built in C# that looks at a particular blob storage account and tries to download a new blob from there if a new blob exists. In our case, we've named the container "w-deploy-newbits" and the blob name is "WebRole.zip". This zip file is built on our build machine by doing a msbuild /t:Publish on our ccproj in order to build a deployable version of our website (_PublishedWebsites\WebRole in our build folder). We zip this file and upload it to the appropriate cloud location from the desktop as part of our build script (powershell with <a href="https://github.com/JamesKovacs/psake">psake</a>), and when the "NewBits" console app sees that new zip, it redeploys.</div><div> </div><div>
<br /></div><div>We have been using this technique for our profiling of our app (for performance) so that we can quickly make changes, redeploy, reprofile, and repeat the process. It has worked VERY well for this. We have no intention of using this for production releases or anything of that matter and would never recommend such a thing.</div><div> </div><div>
<br /></div><div>I'm going to try to make the code available in github, but it will probably take me some time to do so, as I need to remove anything sensitive to our own application setup and I'm really busy right now. However, if you want more details about how we did this, please feel free to contact me via comments or my email address.</div>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-89561855771291775782011-02-20T21:56:00.000-08:002011-02-20T22:03:57.166-08:00Setting up branches to always rebase in gitSo, today at Harmony Hackathon, several people asked me to post the way to set up your branches in git to always rebase. I know this is a controversial thing in git, if you disagree with me on why you might want to rebase, I don't care. I like it and all I'm doing in this post is telling people how to do it if they want to. Don't do it if you're afraid of rebase, you don't want rebase, or you are afraid your friends will shun you for using rebases by default. Don't tell me if you don't like rebase, because I don't care ;)<br /><br />Ok... I'm going to tell you how to set up rebase to ALWAYS rebase on ALL repos. If you don't want this for all repos, then you can do this on a per-repo basis by doing all the config without "--system". Also, you can use "--global" instead of "--system" if you want to ensure that other people that use your machine aren't affected by the setting (nobody else uses my machines, so I don't care and do all my config at the --system level).<br /><br />So, to set up all "master" branches to do rebase by default - do the following:<br /><pre>git config --system branch.master.rebase true</pre><br />To set up all "new" remote tracking branches to be created with rebase by default, do:<br /><pre>git config --system branch.autosetuprebase always</pre><br />If you already have remote tracking branches, then I recommend you make sure you're current, delete the local branches, and recreate them (1.7.x does this automatically if you use the branch name to check out the remote branch, so make sure you're using Git 1.7.x or later for that hawtness (as darkxanthos would say)).Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-26383702155402361352011-02-20T21:21:00.000-08:002011-02-20T21:55:47.028-08:00Resharper: Please indent my shit by 1000 miles, please! kthxbyeSo... Today, while at the Harmony Hackathon, I took a few minutes of yak shaving time to figure out the location of the setting that had all of us at the hackathon cursing resharper from time to time. If you want to see an example of why we were cursing, see a screenshot below from visual studio for one of the methods we were working on.<br /><br /><a href="http://www.flickr.com/photos/59752516@N03/5463633427/" title="resharperlove by kelly.p.leahy, on Flickr"><img src="http://farm6.static.flickr.com/5093/5463633427_111a019986_b.jpg" width="717" height="475" alt="resharperlove" /></a><br /><br />Resharper has a setting that tells it to indent the crap out of array and object initializers, and the default is "please resharper, indent my shit off the screen so far to the right that I run out of scrollbar space if I have more than two nested lambdas or objects". Today we took to rectifying this problem in our configuration.<br /><br />The setting is under formatting style / other / other / indent array, object and collection initializers. You can see it in the screenshot below. Uncheck it and stop bitching about resharper moving your shit 1000 miles to the right ;)<br /><br /><a href="http://www.flickr.com/photos/59752516@N03/5463659909/" title="resharpersettings by kelly.p.leahy, on Flickr"><img src="http://farm6.static.flickr.com/5180/5463659909_6318ee7665_b.jpg" width="700" height="700" alt="resharpersettings" /></a>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-69516121961621642662010-03-18T02:14:00.001-07:002010-03-18T02:24:45.995-07:00Not your mama’s sort algorithm<p>So, not all that long ago, we were profiling our application, and I noticed that a particular part of our system was consuming a huge amount of time sorting arrays of 300,000 to 1M items. I was a little surprised to see that this was taking an inordinate amount of time but it shouldn’t have been so surprising to me, since the keys by which we were sorting these arrays are somewhat complex and so comparisons are nontrivial.</p> <p>The arrays we were sorting were lists of keys (and their associated data), and we were sorting them in a lexicographic ordering that was based on an elsewhere defined sort order (that was defined by the user). The keys are themselves a list of id-value pairs where the ‘id’ is a short integer, and the value is a string. So, as an example, consider the problem of sorting the following list in lexicographic order by the key ids 3, 4, 0 (in that order):</p> <ul> <li>(0 = “UL1”, 3 = “M”, 4 = “S”)</li> <li>(3 = “F”, 0 = “UL4”, 4 = “S”)</li> <li>(4 = “S”, 3 = “M”, 0 = “UL2”)</li> <li>(4 = “N”, 3 = “F”)</li></ul> <p>When looking at the data, a few things stand out. Firstly, the keys aren’t all in the same “internal order”. This is because the keys come from various different places in the system and are created at various different times, and are not sorted because the cost of doing so proved to be a performance issue. Secondly, not all of the keys have all of the key ids (0, 3, and 4). This is a feature of our system where not all keys require all ids. Keys cannot overlap, but a missing ‘id’ is a wildcard, and allows searches to match that key with any value for that id.</p> <p>When doing a comparison between these keys, we were using a IComparer implementation that looks like the following code snippet. The IKeyInstance interface is the interface for the objects that contain one of the rows of the above list of data. GetValue returns the string associated with the id passed as its argument (or null if the value doesn’t exist in the key).</p> <div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"> <div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> KeyInstanceSortedComparer : IComparer<IKeyInstance></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">short</span>[] _KeyIdSortOrder;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #0000ff">public</span> KeyInstanceSortedComparer(IEnumerable<<span style="color: #0000ff">short</span>> keyIdSortOrder)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> _KeyIdSortOrder = keyIdSortOrder.ToArray();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">int</span> Compare(IKeyInstance keyInstanceA, IKeyInstance keyInstanceB)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> <span style="color: #0000ff">foreach</span> (var keyId <span style="color: #0000ff">in</span> _KeyIdSortOrder)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> var keyValueA = keyInstanceA.GetValue(keyId);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> var keyValueB = keyInstanceB.GetValue(keyId);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> var compareResult = <span style="color: #0000ff">string</span>.Compare(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> keyValueA.ToValue(), </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> keyValueB.ToValue());</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> <span style="color: #0000ff">if</span> (compareResult != 0)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> <span style="color: #0000ff">return</span> compareResult;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> <span style="color: #0000ff">return</span> 0;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> }</pre></div></div><br /><p>The _KeyIdSortOrder field is the sort order for the particular sort operation we are performing when using this comparer. The ToValue method is an extension method that returns the value’s string if it’s non-null, or null otherwise. It’s pretty easy to see from this snippet that this code implements a lexicographic sort on the IKeyInstance implementers.</p><br /><p>Now, given the fact that the IKeyInstance implementers did not sort their id-value pairs internally, GetValue has to do a linear search of the key instance in order to find the appropriate value. Additionally, the comparator must compare as many keyId’s values as it encounters until it either reaches the end of the list of id’s to compare, or it finds an id for which the two keys have different values. The keys are never larger than about 25 ids in any key, and they are typically more like 2 – 8 id-value pairs per key. Also, much of the memory consumed in the runtime footprint of our application was at one point held by these IKeyInstance implementers or their associated id-value pairs, so every effort to keep their size down must be made.</p><br /><p>One of the optimizations we made to reduce the size of these key instances was to manufacture the strings used as “values” for the id-value pairs from a factory that coalesced duplicates so that we only ever had one instance of a given value in memory at any point in time. Another optimization was to do the same with id-value pairs themselves. These two combined optimizations had a profound impact on the memory footprint of our application, due to the amount of duplication present in the data, and the limited amount of uniqueness in the key’s values and id-value pairs.</p><br /><p>Anyway, to get back to the sorting, I spent some time looking at the sort performance and realized that no matter what I did, I couldn’t really improve the performance of the sort much, so long as we were continuing to do all these comparisons. The problem is, the comparisons are simply expensive due to the nature of GetValue in IKeyInstance and the fact that we weren’t sorting the entries. We could have used Dictionary in the key instances, but that would have taken up too much memory for our taste. We could have used a sorted list of id/value pairs, but when there are only typically 2-8 columns in the key, binary search would likely only be slightly faster than a linear search, and may have even been slower.</p><br /><p></p><br /><p>So…. I started looking for ways to parallelize the algorithm. One of the other developers on my team – Robert Ream – was looking into parallel implementations of quicksort and mergesort to try to figure out how we could improve the performance of our sorting, since we have a few processors at our disposal on our typical users’ machines. I also asked him to look into ways of making the algorithm cancellable and restartable so that we could ‘stop’ the algorithm in progress, serialize its state to disk, and move on to another item in the UI without losing the work that had already been done. While this was a very interesting problem to solve, it turned out that trying to get rid of the comparisons would bear much more tasty fruit.</p><br /><h3>Thinking outside the box</h3><br /><p>In the process of looking for ways to reduce the number of comparisons, I started talking to Robert about which of the algorithms we knew about had less comparisons (mergesort / quicksort / etc.). After some discussion, we really didn’t end up anywhere useful, so Robert continued working on his parallel quicksort implementation. In the meantime, the weekend came, and I decided to spend a couple hours researching what else could be done. I pulled out Knuth’s TAOCP Vol 3 and started reading. In it, I came across his description of radix sort. I read his description, which didn’t lead me to any great revelations (since he basically said that it doesn’t typically perform that much better than the other algorithms), but I was intrigued by the fact that it didn’t use comparisons.</p><br /><p>I then continued searching for information, reading Cormen, and a couple of other algorithm books I have around the house, and then did some searching online. I came across several publications by Stefan Nilsson on the subject of radix sort, and read some of them. It turns out that after reading a few of these I started to think about our problem a bit more and try to consider whether it could be framed in a structure that allowed us to apply radix sort. Still, the complete lack of comparisons was the motivating factor for me, given my knowledge of the fact that the comparisons were more than 95% of the time spent in our sorting code.</p><br /><p>After a few hours of thought on the subject on Friday, I decided Saturday afternoon to investigate a bit in code. So, if you don’t have any background on radix sort, the basic idea is this – instead of using comparisons, you must have numerical values that can be assigned to the ‘values’ of your key elements being sorted, and the assignment must be in the same order as your sort order. For instance, if you want to sort a bunch of strings, the values are the ASCII values of the characters (or at least their ASCII sort collation order number). Radix sort takes advantage of this fixed space to allow you to do array assignments and simple linked list operations instead of doing comparisons. Radix sort performs best if you have a large ‘alphabet’ of values to work with and your keys are either non-overlapping, or don’t have a very large number of duplicates.</p><br /><p>There are several variations of radix sort algorithms - the two classifications are MSD and LSD approaches. I used the MSD approach since it was easier to understand how it applied to my problem, and I originally thought I had varying length keys so it seemed like it would be more appropriate (LSD approach seems to work better with fixed-length keys).</p><br /><p>In order to apply radix sort to my problem, I needed to be able to assign a value to each key-value in my key. Since I already had a factory that maintained the invariant that there was only one instance of each key-value in memory at any given point in time, and in order to do this, the factory needed to track these objects, I decided to provide the ability for the factory to assign a ‘sort order’ value to each of the key-values. An integer was big enough, and I could put some intelligence in the caching factory to have it only perform the sort when requested AND necessary (due to a change having occurred to the list of key-values).</p><br /><p>Given these sort order values, I could define an “alphabet” of 0..N where N is the number of values in existence and the highest sort order value, 1 is the lowest, and 0 represents a missing id in the key. Then, I could logically think of my keys as looking like ordered M-tuples of integers (where M is the number of IDs in my sort order specification, and the maximum number of id-value pairs to be found in any key). The integers in these M-tuples were the sort order values from the key-value list, and the tuples were logically sorted in the appropriate order for doing the lexicographic sort.</p><br /><p>Then, the keys above could (fictitiously) look something like (in the sort order 3, 4, 0):</p><br /><ul><br /><li>(2, 4, 5) //(0 = “UL1”, 3 = “M”, 4 = “S”)</li><br /><li>(1, 4, 7) //(3 = “F”, 0 = “UL4”, 4 = “S”)</li><br /><li>(2, 4, 6) //(4 = “S”, 3 = “M”, 0 = “UL2”)</li><br /><li>(1, 3, 0) //(4 = “N”, 3 = “F”)</li></ul><br /><p>with the sort order values being:</p><br /><p> (1 = F, 2 = M, 3 = N, 4 = S, 5 = UL1, 6 = UL2, 7 = UL4)</p><br /><p>and the prescribed sort order is, of course</p><br /><ul><br /><li>(1, 3, 0)</li><br /><li>(1, 4, 7)</li><br /><li>(2, 4, 5)</li><br /><li>(2, 4, 6)</li></ul><br /><h3>Implementation of Radix Sort</h3><br /><p>So, armed with this approach of assigning ordinal positions to the values in my keys, and the logical definition of a key as given above, I was in a position to apply radix sort (which requires a desired lexicographic ordering and known ‘cardinal’ values for the letters of the alphabet you are sorting). My implementation breaks into three pieces – an ‘inner’ algorithm (in my case bucketsort), a sorter, and a way to track the unfinished portions of the array being sorted.</p><br /><p>The basic idea of my implementation is that you start with the full array as a single ‘unsorted’ range. The algorithm loops as long as there is work to do, by removing a single unsorted range from a queue, processing that unsorted range, and then moving on to the next step. Once there is no more work to do, the array is sorted.</p><br /><p>The algorithm requires an ‘inner’ sort algorithm, since radix sort processes one ‘digit’ at a time from the source alphabet. In my case, radix sort will process one key-value column at a time, starting from left to right. When processing a column, it will use a working array that has one linked list for each possible value of the digit. For instance, we would have a working array (of linked lists) of length 8 (since there are 7 distinct key values, plus one ‘missing’ key value). The sort will walk linearly through the list putting items into the ‘bucket’ that corresponds to the value for the first column. Then, after it has assigned all items to their appropriate buckets, it will put those items back into the array in the order in which they appear in the array and their linked list. At this point, the array is sorted by the first column, and the work remaining to be processed is the list of buckets of length 2 or more. The process is iteratively applied to each column, ignoring the values of previous columns, but when applying for a column, it is only applied to the subset being currently processed (i.e. after splitting buckets 1 and 2, the ‘remaining work’ will be the ranges (0, 2) and (2, 2), where the tuples are (start, length).</p><br /><p>The work of splitting items into their buckets is mostly done by my ‘RadixSortBucketer’ class, given here:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> RadixSortBucketer</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> LinkedList<<span style="color: #0000ff">int</span>>[] _Buckets;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #0000ff">public</span> RadixSortBucketer(<span style="color: #0000ff">int</span> bucketCount)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> _Buckets = <span style="color: #0000ff">new</span> LinkedList<<span style="color: #0000ff">int</span>>[bucketCount];</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> AddItem(<span style="color: #0000ff">int</span> bucket, <span style="color: #0000ff">int</span> item)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> var list = _Buckets[bucket];</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> <span style="color: #0000ff">if</span>(list == <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> _Buckets[bucket] = list = <span style="color: #0000ff">new</span> LinkedList<<span style="color: #0000ff">int</span>>();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> list.AddLast(item);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> SaveItemsTo(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> LinkedList<RadixSortUnfinishedRange> unsortedRanges, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> <span style="color: #0000ff">int</span>[] workArray, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> <span style="color: #0000ff">int</span> startOffset)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> var targetIndex = startOffset;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> <span style="color: #0000ff">foreach</span>(var list <span style="color: #0000ff">in</span> _Buckets.Where(b => b != <span style="color: #0000ff">null</span>))</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> <span style="color: #0000ff">if</span>(list.Count > 1)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> unsortedRanges.AddLast(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> <span style="color: #0000ff">new</span> RadixSortUnfinishedRange(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> targetIndex, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> list.Count));</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span> <span style="color: #0000ff">foreach</span>(var item <span style="color: #0000ff">in</span> list)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> workArray[targetIndex++] = item;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Clear()</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> <span style="color: #0000ff">for</span>(var i = 0; i < _Buckets.Length; i++)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> _Buckets[i] = <span style="color: #0000ff">null</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 41:</span> }</pre></div></div><br /><p>The array of LinkedList<int> is the buckets, the LinkedList<int> in each bucket is (possibly empty) list of indices that fall into that bucket. The unsorted range object looks like:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> RadixSortUnfinishedRange</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">int</span> Start;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">int</span> Count;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #0000ff">public</span> RadixSortUnfinishedRange(<span style="color: #0000ff">int</span> start, <span style="color: #0000ff">int</span> count)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> Start = start;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> Count = count;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> }</pre></div></div><br /><p></p><br /><p>and the overall algorithm looks like:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> RadixSorter</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">delegate</span> <span style="color: #0000ff">int</span> ValueFunctionDelegate(<span style="color: #0000ff">int</span> level, <span style="color: #0000ff">int</span> item);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> RadixSortBucketer _Bucketer;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">int</span>[] _WorkArray;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> ValueFunctionDelegate _ValueFunction;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">int</span> _LevelCount;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">public</span> RadixSorter(<span style="color: #0000ff">int</span>[] arrayToSort, <span style="color: #0000ff">int</span> bucketCount, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> <span style="color: #0000ff">int</span> levelCount, ValueFunctionDelegate valueFunction)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> _WorkArray = arrayToSort;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> _Bucketer = <span style="color: #0000ff">new</span> RadixSortBucketer(bucketCount);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> _LevelCount = levelCount;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> _ValueFunction = valueFunction;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Sort()</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> var currentLevel = 0;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> var currentUnsortedRanges = <span style="color: #0000ff">new</span> LinkedList<RadixSortUnfinishedRange>();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> currentUnsortedRanges.AddLast(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> <span style="color: #0000ff">new</span> RadixSortUnfinishedRange(0, _WorkArray.Length));</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> <span style="color: #0000ff">while</span>(currentUnsortedRanges.Count > 0)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> var nextUnsortedRanges = <span style="color: #0000ff">new</span> LinkedList<RadixSortUnfinishedRange>();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> <span style="color: #0000ff">foreach</span>(var range <span style="color: #0000ff">in</span> currentUnsortedRanges)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> _Bucketer.Clear();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> <span style="color: #0000ff">for</span>(<span style="color: #0000ff">int</span> i = range.Start, j = 0; j < range.Count; i++,j++)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> _Bucketer.AddItem(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> _ValueFunction(currentLevel, _WorkArray[i]),</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> _WorkArray[i]);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span> _Bucketer.SaveItemsTo(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> nextUnsortedRanges,</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> _WorkArray,</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> range.Start);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 41:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 42:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 43:</span> currentUnsortedRanges = nextUnsortedRanges;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 44:</span> currentLevel++;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 45:</span> <span style="color: #0000ff">if</span>(currentLevel >= _LevelCount)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 46:</span> <span style="color: #0000ff">break</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 47:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 48:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 49:</span> }</pre></div></div><br /><p></p><br /><p>The only ‘special’ part of this is that I have defined a delegate to allow this to be parameterized. The delegate is a value function that returns the integral value corresponding to a particular item and ‘level’ (level is like the ‘current column’ value). In my particular problem, the value function is a delegate that uses the ‘level’ to determine which ‘value’ to ask for, and returns 0 if there is no value, or the sort order of the value if the value is not null. Item is the index into the original array to use when getting values for the algorithm.</p><br /><h3>Conclusion?</h3><br /><p>So, what was the end result of my experimentation, you ask? Well, the sort that used to take 17 seconds or so, now takes under 1 second, and much less if it doesn’t include the cost of assigning the sort orders to the key values (which doesn’t happen for each sort, it only happens for the first sort after a change to the key-value cache – which is pretty infrequent). Could my implementation be more efficient – absolutely. However, I’m a strong believer in “write it the way that makes the most sense to you, and then only make it ‘more efficient’ if profiling shows it to need to be more efficient” – this is basically the philosophy of avoiding ‘premature optimization’. Of course, I don’t condone ‘premature pessimisation’ either, so you shouldn’t choose the wrong algorithm or data structure for the job, but you also shouldn’t micro-optimize every bit of code you write, as you’re writing it.</p><br /><p>I hope you find this description useful – I tried to give details about how & why I did what I did, instead of just ‘what’ I did. Hopefully that makes this an interesting read and provides some insights that just posting the code wouldn’t provide.</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-48754120939511245582010-03-16T22:14:00.001-07:002010-03-16T22:36:57.757-07:00Weak Events<p>A while back I was having a discussion with my friend Tim Erickson (@imerickson) about problems surrounding GC and events in WPF. Specifically, we were talking about problems with ICommand (WPF) implementations. I mentioned to him that we have and use our own weak eventing pattern on my team. I promised to write a blog post about it, but I’ve been slow doing so. This is my attempt to make good on my promise. Hopefully it’s not too awful.</p> <p>The problem that weak events are meant to solve is one in which you an event source (the thing raising the events) that has a potentially longer lifetime than some of the corresponding event sinks (the things receiving the events). In that case, normal .NET events (delegates) will cause the event source to keep the event sinks alive, unless some explicit event removal code is executed on the sinks to tell them to remove their event handlers from the source. In many cases this is relatively easy to do, but in many it is very inconvenient to do so and the programmer would rather not “know” when the removal is necessary.</p> <p>As an example, in our application, we have an editor grid that has bindings that act as an event source, while the grid acts as an event sink. BTW, when I say “bindings” here, think of them as a ‘data source’ for the grid, not ‘bindings’ in the WPF sense. These bindings are not really a problem, as the bindings are created and “attached” to the grid, and when they are “detached” from the grid, the grid removes any event handlers it has attached to the bindings. In this case, we don’t need weak events. However, underlying the grid bindings is a view model object (that is sort of an amalgamation of a view model and a data model object, but that’s an argument for another day) that has events to notify its subscribers if any of the contents of the VM have changed from anywhere in the UI.</p> <p>In our UI, we can have multiple views on the same exact model object (think multiple views in MS Excel or MS Word) that each need to be notified if any one of the views causes a change in the model. In this case, our model is an event source, and our bindings are an event sink. Here’s the problem – our model lives “forever” (at least it lives as long as our application is ‘running’), yet the grid bindings get recreated when the user changes their view, moves to another item, opens a new view, or any number of other changes to the state of the UI. In effect, we create MANY of these grid bindings objects throughout a single run of our application, and we wish to make no effort to track when they are no longer “in use”, since doing so is rather difficult and would litter our code everywhere with code to handle this tracking.</p> <p>However, if we ignore the tracking, and just let the grid bindings “get released” by the grid, etc., the events that were hooked by the bindings from the model in order to get notifications of changes in the model will never get unhooked. This will lead to the model “keeping alive” a huge number of these bindings objects and never releasing them, effectively creating a “memory leak” until the model is shut down. Therefore, we had a choice – either hook the events and try to force appropriate shutdown in all the places it was needed, or find another solution that didn’t involve the bindings hooking events directly on the model.</p> <p>Since we have total control over both the control and the underlying model, we had a lot of flexibility in our choice of solutions to this dilemma. I investigated the .NET (WPF) model for weak events, as well as several other methods people had used on the net. I even found a description from the venerable Jeffrey Richter in his CLR via C# book (and found it to have errors, as a matter of fact). The WPF model is very general and is the way to go if you are building a control and don’t have control over the model that will be hooked to your control (or want to play nice in the WPF data binding way), but it leaves much to be desired in terms of the programming model.</p> <p>The WPF weak event model is centered around the Microsoft.Windows.IWeakEventListener interface and the Microsoft.Windows.WeakEventManager base class. For a discussion of some of the details, see <a title="http://blogs.msdn.com/nathannesbit/archive/2009/05/28/weakeventmanager-and-iweakeventlistener.aspx" href="http://blogs.msdn.com/nathannesbit/archive/2009/05/28/weakeventmanager-and-iweakeventlistener.aspx">http://blogs.msdn.com/nathannesbit/archive/2009/05/28/weakeventmanager-and-iweakeventlistener.aspx</a></p> <p>For the most part, I agree with Nathan’s assessment of this class. I do, however, believe that if you are writing controls to be consumed by applications out of your control, you effectively MUST follow this model (not mine or anyone else’s), since this is the model that is used throughout WPF. While you could implement your own variation of this model, you still need to apply the general concepts that it does in any solution you come up with (i.e. the ‘endpoint’ providing the ‘weakness’ in the events is the event sink, not the event source).</p> <p>For our application, we have adopted the ‘opposite’ pattern – that is, that the weak events should be sourced from the event source as weak, rather than having an intermediary in the weak event manager that handles turning ‘strong’ events from the model into ‘weak’ events (listeners) on the event sink. We like this model because it hides the complexity of dealing with the ‘weakness’ of the events behind the facade of the event handler attachment points and ‘invocations’ in the model. Our model basically works as follows: the event source provides a custom event implementation, and when sinks subscribe to this event, they are added to a weak event “multicast delegate-like” object that is private to the event source. The subscribers don’t know that they are hooking to a weak event. On the other hand, the model raises events using this “special weak event object” and the attached event sinks receive these messages if they are still alive.</p> <p>There is one small wrinkle to our model. Event handlers attached to weak events in this manner MUST be kept around by something else, or else they will get collected and will cease to receive events. This isn’t a problem for us, since it’s exactly what we’d like to see happen, but there can be problems if one isn’t careful, as it will seem like events stop getting ‘caught’ by their intended targets, and you won’t know why.</p> <p>So, now on to the details. First off, we have a ‘WeakDelegate’ class, that is responsible for being a weak version of the Delegate class built in to the .NET framework. It looks like the following:</p> <div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"> <div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> <span style="color: #008000">/// A "weak reference" version of a delegate. This class is used </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #008000">/// by the MulticastWeakDelegate type to support "weak events" </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #008000">/// pattern in the system.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #008000">/// <typeparam name="T">The type of delegate to return.</typeparam></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> WeakDelegate<T></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> <span style="color: #0000ff">where</span> T : <span style="color: #0000ff">class</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> WeakReference _Target;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> MethodInfo _Method;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> <span style="color: #008000">/// Constructs a weak delegate from the provided delegate.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> <span style="color: #008000">/// <param name="source"></param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> <span style="color: #0000ff">public</span> WeakDelegate(Delegate source)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> <span style="color: #0000ff">if</span> (source == <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ArgumentNullException(<span style="color: #006080">"source"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> <span style="color: #0000ff">if</span> (source.Target == <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ArgumentException(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> <span style="color: #006080">"Source points to a null target. "</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> + <span style="color: #006080">"This is not usable as a weak reference."</span>, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> <span style="color: #006080">"source"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> <span style="color: #0000ff">if</span> (source.GetType() != <span style="color: #0000ff">typeof</span>(T))</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ArgumentException(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> <span style="color: #006080">"Source is not the proper delegate type. "</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> + <span style="color: #006080">"The delegate type must match the type passed "</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span> + <span style="color: #006080">"as the type parameter 'T'"</span>,</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> <span style="color: #006080">"source"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> _Target = <span style="color: #0000ff">new</span> WeakReference(source.Target);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> _Method = source.Method;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> <span style="color: #008000">/// Returns the contents of the WeakDelegate as a proper </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> <span style="color: #008000">/// Delegate object, or NULL if the</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> <span style="color: #008000">/// delegate's target has been collected already.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 41:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 42:</span> <span style="color: #0000ff">public</span> T Delegate</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 43:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 44:</span> get</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 45:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 46:</span> var obj = _Target.Target;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 47:</span> <span style="color: #0000ff">if</span> (obj == <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 48:</span> <span style="color: #0000ff">return</span> <span style="color: #0000ff">null</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 49:</span> <span style="color: #0000ff">return</span> (T)(<span style="color: #0000ff">object</span>)System.Delegate.CreateDelegate(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 50:</span> <span style="color: #0000ff">typeof</span>(T), _Target.Target, _Method);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 51:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 52:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 53:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 54:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 55:</span> <span style="color: #008000">/// This property identifies if the delegate target has </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 56:</span> <span style="color: #008000">/// already been collected (false if so). Note: there is a </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 57:</span> <span style="color: #008000">/// possible race condition with this property, so be sure to </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 58:</span> <span style="color: #008000">/// check the return value of the Delegate property for null </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 59:</span> <span style="color: #008000">/// even if IsAlive returned true. This is just a quick way to </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 60:</span> <span style="color: #008000">/// identify as "definitely dead", it cannot be trusted not to </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 61:</span> <span style="color: #008000">/// change between calling it and calling the Delegate method!</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 62:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 63:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">bool</span> IsAlive</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 64:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 65:</span> get { <span style="color: #0000ff">return</span> _Target.IsAlive; }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 66:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 67:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 68:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 69:</span> <span style="color: #008000">/// Checks whether the contents of this weak delegate is the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 70:</span> <span style="color: #008000">/// same as the delegate passed as a parameter. If it </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 71:</span> <span style="color: #008000">/// contains the same target & method combination, then the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 72:</span> <span style="color: #008000">/// function returns true. If the contained "delegate" has </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 73:</span> <span style="color: #008000">/// already been collected, or doesn't match the passed </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 74:</span> <span style="color: #008000">/// delegate, it returns false.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 75:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 76:</span> <span style="color: #008000">/// <param name="other">The delegate to test against.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 77:</span> <span style="color: #008000">/// <returns>True if the delegate is the same as the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 78:</span> <span style="color: #008000">/// target/method contained by this weak delegate, False if </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 79:</span> <span style="color: #008000">/// the weak delegate has already been collected or if the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 80:</span> <span style="color: #008000">/// target/method differs.</returns></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 81:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">bool</span> IsSameAs(Delegate other)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 82:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 83:</span> <span style="color: #0000ff">return</span> ReferenceEquals(other.Target, _Target.Target)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 84:</span> && other.Method.Equals(_Method);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 85:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 86:</span> }</pre></div></div><a href="http://11011.net/software/vspaste"></a><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p>The weak delegate has support for “wrapping” a normal .NET delegate (it, of course, copies the contents of the delegate and throws away the underlying delegate so that it can hold a weak reference to the target of the original delegate). It also has support for asking about the validity of the weak reference, getting the delegate back (or null if it’s not alive anymore), and comparing this delegate to others (used in the ‘remove’ handler of events).</p><br /><p>To implement weak events, we use these weak delegates in a similar way to the MulticastDelegate class’ use in C# (the class that is used for ‘event’ implementation). For this, we have a MulticastWeakEvent generic class that allows us to get as close as possible to declaring an ‘event’ in C#, but the language won’t let us get quite there. Here is our MulticastWeakEvent implementation:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> <span style="color: #008000">/// MulticastWeakEvent is the "weak event" pattern support object. This class </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #008000">/// allows classes that wish to provide weak event registration to do so. One </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #008000">/// of these delegate classes must be created for each event, and the class </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #008000">/// must be given the event type (typically EventHandler&lt;TEventArgs&gt;) and </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #008000">/// the type of the event arguments.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> <span style="color: #008000">/// <typeparam name="TEventType">The type of event to support. This should </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> <span style="color: #008000">/// normally be the .NET 2.0 EventHandler&lt;TEventArgs&gt; type.</typeparam></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #008000">/// <typeparam name="TEventArgs">The type of the event arguments object to use </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> <span style="color: #008000">/// in calls to this event.</typeparam></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> MulticastWeakEvent<TEventType, TEventArgs></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> <span style="color: #0000ff">where</span> TEventType : <span style="color: #0000ff">class</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> <span style="color: #0000ff">where</span> TEventArgs : EventArgs</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> List<WeakDelegate<TEventType>> _List;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> Action<TEventType, <span style="color: #0000ff">object</span>, TEventArgs> _InvokeAction;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> Delegate _AsDelegate(TEventType handler)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> <span style="color: #0000ff">return</span> (Delegate)(<span style="color: #0000ff">object</span>)handler;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> <span style="color: #008000">/// Constructs a new weak event delegate. The action should simply call </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> <span style="color: #008000">/// the delegate with the appropriate arguments. For instance, the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> <span style="color: #008000">/// action should simply be a delegate that looks like </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> <span style="color: #008000">/// <code>(d,s,e) =&gt; d(s,e)</code>.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> <span style="color: #008000">/// <param name="invokeAction">A delegate that invokes its first argument </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span> <span style="color: #008000">/// (the event delegate) passing the second and third arguments as the </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> <span style="color: #008000">/// arguments to the invocation.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> <span style="color: #0000ff">public</span> MulticastWeakEvent(Action<TEventType, <span style="color: #0000ff">object</span>, TEventArgs> invokeAction)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> <span style="color: #0000ff">if</span> (!<span style="color: #0000ff">typeof</span>(Delegate).IsAssignableFrom(<span style="color: #0000ff">typeof</span>(TEventType)))</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> InvalidOperationException(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span> <span style="color: #006080">"TEventType must be a delegate type in MulticastWeakDelegate"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> _List = <span style="color: #0000ff">new</span> List<WeakDelegate<TEventType>>();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> _InvokeAction = invokeAction;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 41:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 42:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 43:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 44:</span> <span style="color: #008000">/// Invokes the event for all listeners, one at a time.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 45:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 46:</span> <span style="color: #008000">/// <param name="sender">The sender to pass to the invocation.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 47:</span> <span style="color: #008000">/// <param name="e">The event args for the invocation.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 48:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Invoke(<span style="color: #0000ff">object</span> sender, TEventArgs e)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 49:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 50:</span> <span style="color: #0000ff">lock</span> (_List)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 51:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 52:</span> _List.RemoveAll(eh => !eh.IsAlive);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 53:</span> _List.ForEach(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 54:</span> eh =></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 55:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 56:</span> var target = eh.Delegate;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 57:</span> <span style="color: #0000ff">if</span> (target != <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 58:</span> _InvokeAction(target, sender, e);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 59:</span> });</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 60:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 61:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 62:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 63:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 64:</span> <span style="color: #008000">/// Adds a handler to the weak event invocation list.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 65:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 66:</span> <span style="color: #008000">/// <param name="handler">The handler delegate.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 67:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> AddHandler(TEventType handler)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 68:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 69:</span> <span style="color: #0000ff">lock</span> (_List)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 70:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 71:</span> _List.RemoveAll(eh => !eh.IsAlive);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 72:</span> _List.Add(<span style="color: #0000ff">new</span> WeakDelegate<TEventType>(_AsDelegate(handler)));</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 73:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 74:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 75:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 76:</span> <span style="color: #008000">/// <summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 77:</span> <span style="color: #008000">/// Removes a handler from the weak event invocation list (and compacts </span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 78:</span> <span style="color: #008000">/// the list).</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 79:</span> <span style="color: #008000">/// </summary></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 80:</span> <span style="color: #008000">/// <param name="handler">The handler delegate.</param></span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 81:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> RemoveHandler(TEventType handler)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 82:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 83:</span> <span style="color: #0000ff">lock</span> (_List)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 84:</span> _List.RemoveAll(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 85:</span> eh => (!eh.IsAlive) || eh.IsSameAs(_AsDelegate(handler)));</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 86:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 87:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 88:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 89:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> MulticastWeakEvent : MulticastWeakEvent<EventHandler, EventArgs></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 90:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 91:</span> <span style="color: #0000ff">public</span> MulticastWeakEvent(): <span style="color: #0000ff">base</span>((d, s, e) => d(s, e))</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 92:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 93:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 94:</span> }</pre></div></div><br /><p></p><br /><p></p><br /><p>Now, given this class, we can show some examples on how to expose weak events. Consider a class that has a “NameChanged” event of type EventHandler<NameChangeEventArgs>. We can declare a subclass of MulticastWeakEvent (not strictly necessary, but helps with readability) as:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> NameChangeEventArgs: EventArgs</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">string</span> OldName;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">readonly</span> <span style="color: #0000ff">string</span> NewName;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #0000ff">public</span> NameChangeEventArgs(<span style="color: #0000ff">string</span> oldName, <span style="color: #0000ff">string</span> newName)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> OldName = oldName;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> NewName = newName;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> NameChangedEvent: </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> MulticastWeakEvent<EventHandler<NameChangeEventArgs>, NameChangeEventArgs></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> <span style="color: #0000ff">public</span> NameChangedEvent()</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> : <span style="color: #0000ff">base</span>((d, s, e) => d(s, e))</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> }</pre></div></div><br /><p>The main reason for declaring a special type for one of these events is that it helps avoid the silly (d,s,e) => d(s, e) crap that we’re forced to do so that we can avoid reflection costs in our code. I couldn’t find a way around this without using reflection, so if someone has any ideas I’d be happy to hear about them (but don’t be surprised if I’m skeptical, since I fought this problem for a very long time).</p><br /><p>Now, to create a model object that raises events of the type NameChanged (transparently), we can do the following:</p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span> IPerson</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">string</span> Name { get; }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #0000ff">event</span> EventHandler<NameChangeEventArgs> NameChanged;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> Person: IPerson</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span> _Name;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> NameChangedEvent _NameChanged = <span style="color: #0000ff">new</span> NameChangedEvent();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> <span style="color: #0000ff">public</span> Person(<span style="color: #0000ff">string</span> nameAtBirth)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> _Name = nameAtBirth;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span> Name</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> get</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> <span style="color: #0000ff">return</span> _Name;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> set</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> <span style="color: #0000ff">if</span>(_Name == <span style="color: #0000ff">value</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> <span style="color: #0000ff">return</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> var oldName = _Name;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> _Name = <span style="color: #0000ff">value</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> _NameChanged.Invoke(</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> <span style="color: #0000ff">this</span>, </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span> <span style="color: #0000ff">new</span> NameChangeEventArgs(oldName, _Name));</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">event</span> EventHandler<NameChangeEventArgs> NameChanged</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span> add { _NameChanged.AddHandler(<span style="color: #0000ff">value</span>); }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> remove { _NameChanged.RemoveHandler(<span style="color: #0000ff">value</span>); }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> }</pre></div></div><br /><p>I think this programming model is pretty clean, and it works well for what we’re doing. The only caveat, as I said earlier, is that you absolutely must understand how GC works when working with these, or else you’ll have some very weird bugs related to event sinks disappearing in the night ;) Allow me to give an example of the “event sink disappearing” problem in case you haven’t figured out what I’m referring to yet. Consider the following code:</p><br /><p></p><br /><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><br /><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> PersonWatcher</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> List<IPerson> _PeopleWithNameChanges = <span style="color: #0000ff">new</span> List<IPerson>();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span> <span style="color: #0000ff">public</span> IEnumerable<IPerson> GetPeopleWithNameChanges()</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">return</span> _PeopleWithNameChanges;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">class</span> Listener</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">readonly</span> List<IPerson> _TargetList;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> <span style="color: #0000ff">public</span> Listener(List<IPerson> targetList)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> _TargetList = targetList;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> HandleNameChanged(<span style="color: #0000ff">object</span> sender, NameChangeEventArgs args)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span> _TargetList.Add((IPerson)sender);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> ListenToPerson(Person personToListenTo)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> var listener = <span style="color: #0000ff">new</span> Listener(_PeopleWithNameChanges);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> personToListenTo.NameChanged += listener.HandleNameChanged;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> }</pre></div></div><br /><p>The code above (while only an example) could, or at least some variation of it could very well arise in ‘real life’. One might think they are decoupling the ‘listening’ aspect from the tracking aspect of the problem. The problem with this is that since the Person sources ‘weak’ events, there are no objects that are ‘live’ that are holding strong references to the ‘listener’ instances created on line 27 of the snippet. This means that as soon as the GC decides to do so, the listener will get collected, and you won’t receive the events you expected to receive.</p><br /><p>One way around this is for something ‘live’ to hold on to the listeners until their work is done. For instance, if the “PersonWatcher” class held on to a list of all its listeners, this would keep them alive. Of course, this has the other problem of requiring you to release them at some point in order to allow them to get GCed, but that may not be such a big deal, considering you might know better when to do that release than you know when to ‘detach’ the event listeners. In this particular example, I suspect it’s a wash.</p><br /><p>Anyway, I hope you enjoyed this brief tour of our island of code surrounding weak references. It has served us well thus far, even with the caveats I keep mentioning. Until next time – happy coding!</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com1tag:blogger.com,1999:blog-8594690550975135798.post-78446158917117937602009-12-18T18:36:00.000-08:002009-12-18T18:38:13.764-08:00Creating a remote branch in GitI just had to do this today, and I can't believe how unintuitive it is... At least I found this <a href="http://www.zorched.net/2008/04/14/start-a-new-branch-on-your-remote-git-repository/">post</a>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-14695978621191727422009-02-22T15:59:00.000-08:002009-02-22T16:01:48.739-08:00Binding to SelectedItems in WPF ListBox.Here's a very interesting approach to solving the "can't bind to selected items" issue with the WPF ListBox control. I'll definitely want to look into this more later, as I haven't had a chance to yet. I'm still fighting issues getting CheckedItems initialized on my CheckListBox control that I've written and it suffers from the same issue as SelectedItems in ListBox.<div><br /></div><div>Alex's blog has the approach I'm referring to. <a href="http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!133.entry">Check it out.</a></div>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com1tag:blogger.com,1999:blog-8594690550975135798.post-69145942935429119612009-02-21T23:11:00.001-08:002009-02-21T23:19:09.472-08:00Holy Crap! HyperV versus nVidia drivers - everyone loses!<p>Wow, I've been trying to figure out why WPF apps are soooo slow on my machine for months now. I just chalked it up to Server 2008 being a server OS and having all the goodies for fast rendering turned off, or something like that.</p><p>Today, I stumbled across a post on a forum about problems with HyperV causing video and other apps that "use" video to be really slow on startup and run very sluggish, causing problems with audio and other side effects. It turns out that many (most) video drivers do not play well with HyperV (or HyperV doesn't play well with most video drivers - I'll let MS & the vendors argue over which is the correct statement, I'm staying out of the middle). As such, when running HyperV, the machine can be very sluggish when apps do anything that accesses the video hardware (obviously something that VS2008, VLC media player, and WPF applications do a lot of).</p><p>Finally, I bit the bullet and decided to uninstall HyperV and after doing so, my apps immediately run way faster. I'm not sure exactly how much faster, but I know that when running my UITestHarness app (part of the project I work on), the app would start up rather quickly, but when clicking on a button that shows a WPF window (the app is WinForms mainly), it would take a good 15 seconds to show the window. Now, those windows show immediately (less than 1 second). I find this rather amazing that there are such perf differences between HyperV and non HyperV configs. Oh well, hopefully they'll fix them soon, or I'll find a video card that doesn't have problems (reportedly ATI cards are often in better shape), 'cause I really like HyperV!</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-57806398080443646112009-01-30T21:16:00.001-08:002009-01-30T21:56:21.573-08:00IDisposable and Garbage Collection<p>I've had several discussions recently with friends and associates about garbage collection, IDisposable, and the disposal pattern recommended by Microsoft.  These discussions have brought to light many misconceptions, a few incorrect statements, and some good and bad advice.  In particular, the 'final straw' that led me to write this blog post is the <a href="http://www.codinghorror.com/blog/archives/001211.html" target="_blank">post</a> by Jeff Atwood (@CodingHorror) on his blog, and the <a href="http://agilology.blogspot.com/2009/01/why-dispose-is-necessary-and-other.html" target="_blank">response</a> by Jeff Tucker on his blog (Agilology).</p><p>I tend to lean more towards agreeing with Jeff Atwood (@CodingHorror)'s post, in that I believe that calling Dispose is absolutely an optimization.  That said, it's an optimization that in some cases simply should not be optional for all but those people who understand the precise implications of not doing so.  For instance, SqlConnection.  It's absolutely best practice to dispose of SqlConnection as soon as you can do so in applications that might use connection pooling.  On the other hand, there are resources such as FileStream, DataSet, and the many WaitHandle-derived types that you <em>may</em> want to dispose of early, but it's absolutely an optimization (either in terms of memory footprint or resource contention) to do so early, not a requirement.</p><p>Among the things that can be said about IDisposable and GC, there are a few that I want to get out of the way first.  First of all, the GC does not care about IDisposable <u>at all</u>!  It is simply not aware of whether your class implements IDisposable or not.  That said, many classes that implement IDisposable also implement a finalizer (discussed later), of which the GC is intimately aware.</p><h3>Garbage Collection</h3><p>First, before I go too far, let me make a brief description (simplified, of course) of the garbage collector and the process it follows to collect your unused objects.  Basically put, you go along happily creating new objects from the managed heap, and the GC follows behind you and computes which of those objects are no longer 'reachable' from your program and cleans up after you.</p><p>The GC divides the garbage collection process into stages, called "generations".  All objects start their lifetime in generation 0.  Objects that "survive" a GC in generation 0 move to generation 1.  Those that survive generation 1 move to generation 2.  Earlier generations are collected more frequently than later generations.</p><p>The collection pass is actually two passes - the marking pass, and the reclamation (collection) pass.  The marking pass is where the GC goes through all GC roots in the application and marks 'reachable' objects and all objects reachable from them as "live".  The collection pass is where the GC goes through all objects in the generation(s) being collected and frees those objects that aren't being collected (it may also relocate objects to compact memory if it decides this is useful).</p><p>There's a bit more complexity to it when finalizers are involved.  When an 'unmarked' object to be collected contains a finalizer that hasn't been suppressed using GC.SuppressFinalize(this) (presumably in IDisposable.Dispose) then the object is not freed, but rather is moved from the finalization queue to the freachable queue - and is marked (and all objects reachable from it are also marked).  Obviously the computation is done in a way that makes sense, not iteratively as it sounds from my description, but that complexity is not important to our discussion.</p><p>For more information on the GC, I recommend Jeffrey Richter's <a href="http://www.amazon.com/CLR-via-Second-Pro-Developer/dp/0735621632" target="_blank">CLR via C#</a>, an excellent book on this and many other topics related to advanced .NET programming.  However, I would caution you against reading his "Weak Events" example in that chapter as it is wrong (my next blog post will describe why and how to detect the invalidity of his approach).  Also, there are several blogs that are good for GC and other debugging bits, such as <a href="http://blogs.msdn.com/maoni" target="_blank">Maoni</a>'s or <a href="http://blogs.msdn.com/Tess" target="_blank">Tess</a>'s blogs.  A good start might be Tess's post <a href="http://blogs.msdn.com/tess/archive/2008/04/17/how-does-the-gc-work-and-what-are-the-sizes-of-the-different-generations.aspx" target="_blank">here</a>. (special thanks to my buddy Rich Lang for his tips on resources to recommend).</p><h3>IDisposable and its uses</h3><p>There are several different reasons people might use IDisposable.  Of them, there are two that are the most popular and probably the only ones that "normal" developers should ever put into action.  The first, most obvious, use of IDisposable is when your object needs to own 'unmanaged' resources, either directly or indirectly.  For instance, if you're writing a .NET class that manages some resource that you obtain via a P/Invoke call to some unmanaged library and you need to "free" or "release" that resource when your object is no longer being used.  In this case (direct ownership of unmanaged resources), IDisposable is not strictly necessary, but a finalizer <em><strong>is absolutely necessary</strong></em>.  If the object you're trying to manage is a Win32 handle (closed by CloseHandle), you should probably look at the SafeWaitHandle and SafeFileHandle classes, as well as the SafeHandleMinusOneIsInvalid and SafeHandleZeroOrMinusOneIsInvalid classes.</p><p>The other popular use of IDisposable is for RAII (a C++ concept - "resource acquisition is initialization", whereby a resource is acquired as a constructor call, and released when it goes out of scope - i.e. a using statement body).  An example of this usage is the TransactionScope object, where you acquire the transaction by "newing up" a TransactionScope object (in a C# "using"), and you release it when you exit the using statement.</p><p>I'll take these two uses in turn.</p><h4>Resource Ownership</h4><p>I call the first of the two use cases for IDisposable "Resource Ownership" as your object is the consumer of some resource either directly or indirectly and should free those resources when applicable.  There are two forms of resource ownership, direct and indirect.  Direct is, as it sounds, when your object has direct ownership over a resource.  If the resource is unmanaged (that's really what we're talking about here), you <strong><em>must</em></strong> implement a finalizer for your object, and in that finalizer you should dispose of the resource.  Also, since your finalizer is only executed when your object is collected, it's generally a good idea to give users of your object the opportunity to release the resource 'early'.  For this reason, you implement IDisposable and the <em>disposal pattern </em>(described below).</p><p>If, on the other hand, you only have <em>indirect</em> ownership of unmanaged resources, you don't need a finalizer.  Instead, you should only provide the IDisposable interface and implement the <em>disposal pattern</em>.  If you provide a finalizer when it isn't needed, you will, in effect, be delaying the GC cleanup of your object unnecessarily, since objects with finalizers survive <u>at least</u> two collections after they are freed, if not more.</p><h4>RAII in C#</h4><p>I refer to the second of the two cases for IDisposable as "Resource Acquisition".  The canonical example for this is TransactionScope, in my mind, but another example is a Mutex acquisition class (not to be confused with the FCL's Mutex class) or any other similar class.  These classes can make your code much easier to maintain and read if used properly, but can introduce some very difficult to detect bugs if used improperly, so use them with caution.  For this use of IDisposable, you aren't really using IDisposable because you <em>own</em> resources, but rather because you're building a class that should have <em>acquire/release</em> semantics and the syntax for doing so with C#'s "using" statement is very nice and clean.</p><p>There are several places where this pattern is used, TransactionScope is the one that comes to mind for me in the FCL, but Oren (Ayende Rahien) uses this pattern in Rhino Mocks and you see it in several other frameworks.  Jeffrey Richter describes it in his book in the chapter on memory management, and makes the recommendation that if you are building a library to be used by others outside of your production code, you should make your RAII (my name, not his) objects reference types, but if you are using them only internally, they can be made very efficient through the use of value types that implement IDisposable.  If you are writing these types for libraries, you should take great care to ensure that they free the resources they own <em>at most</em> once, and should think very carefully about whether these types need a finalizer (I believe the jury is out on this one, but I'd say they do and should implement the <em>disposal pattern</em> just as if they were managing a unmanaged resource).</p><p>It should be noted that while I call this "RAII in C#" it really isn't quite the same as RAII in C++, since in C++ destructors are guaranteed to be called when the object goes out of scope, whereas there's really nothing in the C# language / compiler that requires you put these objects in a "using" block, and thus there's nothing that guarantees that their Dispose() method gets called automatically if you choose not to use the using construct.</p><h3>The <em>disposal pattern</em> and its canonical implementation</h3><p>Many resources describe the canonical implementation of IDisposable via the <em>disposal pattern, </em>so I'm not going to go into excruciating detail here.  I'll describe the basics of the pattern and refer the reader to other sources for exact details.  The core idea of the <em>disposal pattern</em> is that there are two ways in which you might want to cleanup after your object: finalization and explicit disposal via IDisposable.Dispose.  If your object is one that should have a finalizer, then the <em>disposal pattern</em> should <strong>absolutely</strong> be followed.  If you don't have a finalizer, then you don't strictly need to follow the disposal pattern (often this is the case for RAII applications that don't acquire resources that will deadlock the application if they aren't released, or those that are guaranteed to be properly used - because you're writing both the object and all code that uses it).  Even so, you're probably best to follow the <em>disposal pattern</em> every time you implement IDisposable and just leave the parts of the pattern empty that don't apply to your particular application.</p><p>The basic rules of the disposal pattern are: </p><ol><li>finalizers should not refer to other managed objects during finalization, since those objects may have already had their finalizers called. </li><li>IDisposable.Dispose() should call IDisposable.Dispose on any objects owned by the implementing object that are IDisposable. </li><li>IDisposable.Dispose() and the finalizer should <strong><em>BOTH</em></strong> free any unmanaged resources owned by the object. </li><li>IDisposable.Dispose() should call GC.SupressFinalize(this) to mark the work of the finalizer as being already done. </li><li>if your objects have shared state, then the finalizers should have code to guarantee that two finalizers being called at the same time is thread-safe. </li><li>finalizers should not assume they are being called on any particular thread - therefore they <strong>cannot</strong> access TLS (thread-local storage) in any way, shape, or form! </li><li>calling IDisposable.Dispose() shouldn't throw an exception if called more than once. </li><li>method calls to any methods other than disposal methods (or the finalizer) should throw ObjectDisposedException if Dispose (or the finalizer) has already been called. </li></ol><p>Microsoft's recommended approach for implementing the <em>disposal pattern</em> is to have a non-public (protected) virtual (unless your class is sealed) method called Dispose on your object that takes a boolean argument called "disposing".  This method should be called both by the finalizer and by IDisposable.Dispose, and if you implement a "Close" convenience method or some other method that does the same thing as IDisposable.Dispose, that should also call this single-argument version of Dispose.  In this Dispose method, your class should free any unmanaged resources, and if "disposing" is true, should also call Dispose on any IDisposable members of your class.  It should also set a flag so you know to throw ObjectDisposedException when any of your other methods or properties are accessed.  Finally, the Dispose method should call GC.SuppressFinalize(this) to notify the GC that the finalizer need not be called.  Then, the object should implement the finalizer as a call to Dispose with disposing = false, and IDisposable.Dispose as a call to Dispose with disposing = true.</p><p>Sample code is as follows:</p><div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4"><div style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 1:</span> <span style="color: #0000ff">class</span> MyClass: IDisposable</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 2:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 3:</span> [DllImport(...)] <span style="color: #008000">// assume this is correctly specified.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 4:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">extern</span> <span style="color: #0000ff">void</span> Free(IntPtr handle);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 5:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 6:</span> <span style="color: #0000ff">private</span> IntPtr _UnmanagedThing;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">private</span> FileStream _LogFile;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 8:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 9:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">bool</span> _IsDisposed;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 10:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 11:</span> <span style="color: #0000ff">public</span> MyClass(FileStream logFile, IntPtr unmanagedThing)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 12:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 13:</span> <span style="color: #008000">// check arguments and don't allow finalizer if they aren't valid.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 14:</span> GC.SupressFinalize(<span style="color: #0000ff">this</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 15:</span> <span style="color: #0000ff">if</span>(logFile == <span style="color: #0000ff">null</span>)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 16:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ArgumentNullException(<span style="color: #006080">"logFile"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 17:</span> <span style="color: #0000ff">if</span>(unmanagedThing == IntPtr.Zero)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 18:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ArgumentException(<span style="color: #006080">"unmanaged thing is invalid!"</span>,</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 19:</span> <span style="color: #006080">"unmanagedThing"</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 20:</span> GC.ReRegisterForFinalize(<span style="color: #0000ff">this</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 21:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 22:</span> _UnmanagedThing = unmanagedThing;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 23:</span> _LogFile = logFile;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 24:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 25:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 26:</span> <span style="color: #0000ff">protected</span> <span style="color: #0000ff">virtual</span> <span style="color: #0000ff">void</span> Dispose(<span style="color: #0000ff">bool</span> disposing)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 27:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 28:</span> <span style="color: #008000">// we can skip doing anything if it's already been done.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 29:</span> <span style="color: #0000ff">if</span>(_IsDisposed)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 30:</span> <span style="color: #0000ff">return</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 31:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 32:</span> <span style="color: #0000ff">if</span>(disposing)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 33:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 34:</span> <span style="color: #008000">// dispose of managed resources here, since we</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 35:</span> <span style="color: #008000">// were called from IDisposable.Dispose()</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 36:</span> _LogFile.Dispose();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 37:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 38:</span> <span style="color: #008000">// make sure we know that we're disposed for other calls.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 39:</span> _IsDisposed = <span style="color: #0000ff">true</span>;</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 40:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 41:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 42:</span> <span style="color: #008000">// free unmanaged resources in either case (IDisposable or</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 43:</span> <span style="color: #008000">// Finalize) and make sure the finalizer doesn't get called</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 44:</span> <span style="color: #008000">// later by the GC.</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 45:</span> Free(_UnmanagedThing);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 46:</span> GC.SuppressFinalize(<span style="color: #0000ff">this</span>);</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 47:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 48:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 49:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Dispose() { Dispose(<span style="color: #0000ff">true</span>); }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 50:</span> ~MyClass() { Dispose(<span style="color: #0000ff">false</span>); }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 51:</span>  </pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 52:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> DoSomething()</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 53:</span> {</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 54:</span> <span style="color: #008000">// some function not related to disposal of the object,</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 55:</span> <span style="color: #008000">// but requiring valid state...</span></pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 56:</span> <span style="color: #0000ff">if</span>(_IsDisposed)</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 57:</span> <span style="color: #0000ff">throw</span> <span style="color: #0000ff">new</span> ObjectDisposedException();</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #606060"> 58:</span> }</pre><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: white; border-bottom-style: none"><span style="color: #606060"> 59:</span> }</pre><br /><br /></div></div><p>Again, if you are working with a handle that is typical of Win32, you should look at the classes mentioned above (SafeHandle and it's family of derived-classes) as they do much of the work for you.  You should also probably have a look at CriticalFinalizerObject as well, and the MSDN topic "Safe Handles and Critical Finalization", especially if you expect your code to be run in a hosted environment other than traditional .NET applications (i.e. IIS7, COM+, SQL Server, etc.).</p><h3>So what's the point?</h3><p>So, now, as I reread the beginning of my blog post, I wonder - what was the point I was trying to make?  Well, it's basically this - the use of IDisposable to dispose of objects early is an optimization, assuming the <em>disposal pattern</em> was correctly implemented by author of the objects you are calling.  That is, unless you are calling RAII-style objects, in which case forgetting to call Dispose and not using these objects in a using block could be disastrous to your program.  On the other hand, there are several cases where it's extremely important to dispose of objects as soon as you are done with them, for instance when dealing with SqlConnection.  </p><p>On the other hand, there are plenty of objects that are fine to allow GC to collect them and "finalize" them, and there is little or no perf impact to doing so (possibly even a positive impact of not forcing early cleanup).  As a for-instance, consider a managed class that wraps an unmanaged resource that is not subject to contention (like some sort of unmanaged object in a library that is using the C/C++ heap to allocate these objects).  If you create a large number of these objects, but are not at risk of running out of memory, it can be much faster to allow the GC to collect these objects (through finalization) than having your code call Dispose on all of them and forcing early cleanup (forcing the application to incur the cost of freeing this unmanaged memory on your user threads instead of the finalizer thread).</p><p>As with any of my posts and most of the advice on the CLR in general, the most important takeaway from this blog post should be "learn the details and use your own judgement".  Happy coding.</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-52806291367583233292009-01-23T00:21:00.001-08:002009-01-23T00:33:52.455-08:00Detecting if other instances of your app are running.<p>I got a question from a friend of mine the other day about why Process.GetProcesses() was returning an error when used under Terminal Services by a user that doesn't have the Debug Programs privilege. It was because Process.GetProcesses() was trying to return the list of ALL processes running on the machine (including those not started by the current user). This worked just fine when a single user was logged into the TS server, but when more than one user was logged in it failed. My friend was very confused by this, until I told him that non-admin users need the Debug Programs privilege in order to open the process token of a process not created in their session.</p> <p>Of course, his next question was "how do I get Process.GetProcesses() to return only the processes accessible to the current user's session?". At that point I took a step back and asked "why?".</p> <p>In fact, what he was trying to do was to detect whether another instance of his application was running, in order to gracefully tell the user that only one instance of the program can run at a time. I asked him why he wasn't using a Mutex and the answer surprised me, just a bit. He said "we were worried about what would happen if the process unexpectedly exited". My next question was yet another "why?".</p> <p>Apparently there's some confusion about how Mutexes work in Windows, as well as how the Mutex method that I've used for about 10 years to detect multiple instances of an app works. I'm not sure where I first learned this technique from, but I've been using it since my Win32 programming days and it's worked like a charm ever since then. I've now used it in several reincarnations (from VB6, Delphi, C++, and now .NET) and it's never failed me.</p> <p>The first misconception that almost everyone has when hearing about this approach is that it has something to do with Mutexes (at least in the way they are used in multithreaded programs). In fact, any Kernel named object could be used for this approach, including even a "delete on close" file. However, I've always used mutexes because that's what was recommended to me, and I don't think it makes much difference since you never actually 'wait' on the mutex so it's not really important what type of object it is. The idea of this technique is not to use a mutex, but to use the 'kernel namespace'. Essentially, by creating a mutex in the naming scope appropriate for your purposes, you are 'registering' that name with the OS. Then, if you were the first one to register the name, you continue, otherwise you show your message and quit.</p><p>The pattern looks like this:</p><pre class="code"><span style="color:blue;">bool </span><span style="color:#010001;">createdNew</span>;<br /><span style="color:#2b91af;">MutexSecurity </span><span style="color:#010001;">ms </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">MutexSecurity</span>();<br /><span style="color:#2b91af;">MutexAccessRule </span><span style="color:#010001;">mar </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">MutexAccessRule</span>(<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">SecurityIdentifier</span>(<span style="color:#2b91af;">WellKnownSidType</span>.<span style="color:#010001;">WorldSid</span>, <span style="color:blue;">null</span>),<br /> <span style="color:#2b91af;">MutexRights</span>.<span style="color:#010001;">FullControl</span>,<br /> <span style="color:#2b91af;">AccessControlType</span>.<span style="color:#010001;">Allow</span>);<br /><span style="color:#010001;">ms</span>.<span style="color:#010001;">AddAccessRule</span>(<span style="color:#010001;">mar</span>);<br /><span style="color:#2b91af;">Mutex </span><span style="color:#010001;">m </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">Mutex</span>(<br /> <span style="color:blue;">false</span>,<br /> <span style="color:#a31515;">@"Global\somenameuniquetomyapplication"</span>,<br /> <span style="color:blue;">out </span><span style="color:#010001;">createdNew</span>,<br /> <span style="color:#010001;">ms</span>);<br /><span style="color:blue;">if</span>(!<span style="color:#010001;">createdNew</span>)<br />{<br /> <span style="color:green;">// show a message that only one instance is allowed and exit<br /> </span><span style="color:blue;">return</span>;<br />}</pre><p>where the first part (the MutexSecurity stuff) guarantees that anybody can access the mutex (the security probably doesn't need FullControl, but I didn't want to think about what it really wants, so I left it at that, no real security risk here since the only thing that could happen is other apps could grab hold of this mutex and use it for their own purposes, but that's not really going to do much). The point of this is that the other instances of your app may not be started by the same user - this guarantees that only one app per machine is startable (because I'm using the Global namespace for my mutex name). If, on the other hand, you want a per-session limit of a single instance (rather than per-machine) you could use the local namespace (change Global\ to Local\), which is local to each TS session.</p><p>The second part of that mess is the mutex creation code. This code (as written) will create the mutex if it doesn't exist or return the existing one if one already exists with that name (and you have permissions to open it). If you created it (because it didn't exist), then 'createdNew' will be set to True. Otherwise, it will be false. Either way, you get a valid Mutex object.</p><p>Then, you should keep this Mutex object alive for the lifetime of your application (i.e. save "m" in a variable that has the same lifetime as your application so that it doesn't get GCed and disappear on you). A good way to do this is to make it a local variable in your Program.cs's main function. When your application is done shutting down, you can close the mutex either as soon as you think it's ok for other instances to start, or let Windows reclaim the mutex for you.</p><p>Generally, I put this code in a separate library function that I can call from everywhere. That function generally looks something like the following (in .NET):</p><pre class="code"><span style="color:gray;">/// <summary><br />/// </span><span style="color:green;">Attempts to create an application isolation mutex, and<br /></span><span style="color:gray;">/// </span><span style="color:green;">return it to the caller. If the caller isn't the first<br /></span><span style="color:gray;">/// </span><span style="color:green;">to create the mutex (i.e. it already exists) then we<br /></span><span style="color:gray;">/// </span><span style="color:green;">return null to indicate that the caller "lost".<br /></span><span style="color:gray;">/// </summary><br />/// <param name="objectName"></span><span style="color:green;">The name to use for the mutex,<br /></span><span style="color:gray;">/// </span><span style="color:green;">should begin with Global\ if you want per-machine<br /></span><span style="color:gray;">/// </span><span style="color:green;">isolation, or Local\ if you want per-session isolation.<br /></span><span style="color:gray;">/// </span><span style="color:green;">If you want per-machine/per-user isolation (slightly<br /></span><span style="color:gray;">/// </span><span style="color:green;">different from per-session) then you should mangle the<br /></span><span style="color:gray;">/// </span><span style="color:green;">mutex name by putting the username in it somewhere.</span><span style="color:gray;"></param><br />/// <returns></span><span style="color:green;">Null if the mutex already existed, or the mutex<br /></span><span style="color:gray;">/// </span><span style="color:green;">if it was created by this function. You should keep the<br /></span><span style="color:gray;">/// </span><span style="color:green;">mutex in scope somewhere until you are ready to release<br /></span><span style="color:gray;">/// </span><span style="color:green;">the isolation. You shouldn't use this mutex for locking<br /></span><span style="color:gray;">/// </span><span style="color:green;">or anything else - just forget it's a mutex altogether.<br /></span><span style="color:gray;">/// </returns><br /></span><span style="color:blue;">public </span><span style="color:#2b91af;">IDisposable </span><span style="color:#010001;">GetAppIsolationHandle</span>(<span style="color:blue;">string </span><span style="color:#010001;">objectName</span>)<br />{<br /> <span style="color:green;">// setup the mutex security settings.<br /> </span><span style="color:blue;">var </span><span style="color:#010001;">ms </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">MutexSecurity</span>();<br /> <span style="color:blue;">var </span><span style="color:#010001;">sidWorld </span>=<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">SecurityIdentifier</span>(<span style="color:#2b91af;">WellKnownSidType</span>.<span style="color:#010001;">WorldSid</span>, <span style="color:blue;">null</span>);<br /> <span style="color:blue;">var </span><span style="color:#010001;">mar </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">MutexAccessRule</span>(<br /> <span style="color:#010001;">sidWorld</span>,<br /> <span style="color:#2b91af;">MutexRights</span>.<span style="color:#010001;">FullControl</span>,<br /> <span style="color:#2b91af;">AccessControlType</span>.<span style="color:#010001;">Allow</span>);<br /> <span style="color:#010001;">ms</span>.<span style="color:#010001;">AddAccessRule</span>(<span style="color:#010001;">mar</span>);<br /><br /> <span style="color:green;">// create the mutex and return it if it's "ours".<br /> </span><span style="color:blue;">bool </span><span style="color:#010001;">createdNew</span>;<br /> <span style="color:blue;">var </span><span style="color:#010001;">mutex </span>= <span style="color:blue;">new </span><span style="color:#2b91af;">Mutex</span>(<br /> <span style="color:blue;">false</span>,<br /> <span style="color:#010001;">objectName</span>,<br /> <span style="color:blue;">out </span><span style="color:#010001;">createdNew</span>,<br /> <span style="color:#010001;">ms</span>);<br /> <span style="color:blue;">if </span>(<span style="color:#010001;">createdNew</span>)<br /> <span style="color:blue;">return </span><span style="color:#010001;">mutex</span>;<br /><br /> <span style="color:green;">// return null if the mutex isn't "ours".<br /> </span><span style="color:blue;">return null</span>;<br />}</pre><p>Then, you can call this code in your Program.cs as follows:</p><pre class="code"><span style="color:blue;">var </span><span style="color:#010001;">isolationHandle<br /> </span>= <span style="color:#010001;">GetAppIsolationHandle</span>(<span style="color:#a31515;">@"Global\MyApp"</span>);<br /><span style="color:blue;">if </span>(<span style="color:#010001;">isolationHandle </span>== <span style="color:blue;">null</span>)<br />{<br /> <span style="color:#2b91af;">MessageBox</span>.<span style="color:#010001;">Show</span>(<span style="color:#a31515;">"Sorry, only one at a time!"</span>);<br /> <span style="color:blue;">return</span>;<br />}<br /><span style="color:blue;">using</span>(<span style="color:#010001;">isolationHandle</span>)<br />{<br /> <span style="color:#2b91af;">Application</span>.<span style="color:#010001;">EnableVisualStyles</span>();<br /> <span style="color:#2b91af;">Application</span>.<span style="color:#010001;">Run</span>(<span style="color:blue;">new </span><span style="color:#2b91af;">Form</span>());<br />}</pre><p>which, of course, looks really nice (at least in my opinion). If you had special "shutdown" stuff to do after Run returns, you can put that outside the using block (assuming it doesn't need to be isolated).</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-10632306756836873152008-11-02T23:33:00.001-08:002009-01-30T21:26:14.272-08:00Getting the message count from an MSMQ Queue<p>While at KaizenConf (<a href="http://www.kaizenconf.com">http://www.kaizenconf.com</a>) today, I was attending a session held by Chris Patterson (twitter: @PhatBoyG) & Dru Sellers (twitter @drusellers) on ESBs and MassTransit, their ESB implementation.</p> <p>During the session, Dru complained that there wasn't a good way to get the message count from an MSMQ queue.  Of course, I was required to take that as a challenge, since that's the kind of guy I am :).  I found that as of the 3.0 API for MSMQ (apparently, that's the XP / Server 2003 vintage), there's a few ways to get the message count for a queue.</p> <p>The available methods (that I found) for asking for the count of messages in a Queue were:</p> <ul> <li>Call MQMgmtGetInfo API to query the queue for the PROPID_MGMT_QUEUE_MESSAGE_COUNT property. </li> <li>Load up a MSMQManagement COM object, call it's Init method to associate with a Queue, and then ask for it's MessageCount property value. </li> </ul> <p>It appears that the first method (which is an API call) is actually just a proxy for the second, so I'm not going to talk about it.  Calling the COM object from .NET is much easier than calling the API anyway, since it's not exactly a 'pretty' API for P/Invoke purposes.</p> <p>Since I'm not really interested in investing a lot of time in this blog post, I'm just going to paste the code here and let you do with it as you please...  Here goes...</p> <pre class="code"><span style="color: blue">var </span>path = <span style="color: #a31515">@".\Private$\foo"</span>;<br /><span style="color: #2b91af">MessageQueue </span>mq = <span style="color: #2b91af">MessageQueue</span>.Exists(path) <br /> ? <span style="color: blue">new </span><span style="color: #2b91af">MessageQueue</span>(path) <br /> : <span style="color: #2b91af">MessageQueue</span>.Create(path);<br /><span style="color: blue">try<br /></span>{<br /> <span style="color: green">// try to insert a few items into the queue...<br /> </span>mq.Send(<span style="color: #a31515">"foo"</span>);<br /> mq.Send(<span style="color: #a31515">"bar"</span>);<br /> mq.Send(<span style="color: #a31515">"foo"</span>);<br /><br /> <span style="color: blue">var </span>msmqMgmt = <span style="color: blue">new </span><span style="color: #2b91af">MSMQManagement</span>();<br /> <span style="color: blue">object </span>machine = <span style="color: blue">null</span>; <span style="color: green">// mq.MachineName;<br /> </span><span style="color: blue">object </span>queuename = mq.Path;<br /> <span style="color: blue">object </span>formatname = <span style="color: blue">null</span>; <span style="color: green">//mq.FormatName;<br /> </span>msmqMgmt.Init(<span style="color: blue">ref </span>machine, <span style="color: blue">ref </span>queuename, <br /> <span style="color: blue">ref </span>formatname);<br /> <span style="color: blue">int </span>messageCount = msmqMgmt.MessageCount;<br /><br /> <span style="color: #2b91af">MessageBox</span>.Show(<span style="color: blue">string</span>.Format(<span style="color: #a31515">"Queue has {0} items"</span>, <br /> messageCount));<br />}<br /><span style="color: blue">finally<br /></span>{<br /> <span style="color: blue">string </span>path2 = mq.Path;<br /> mq.Close();<br /> <span style="color: #2b91af">MessageQueue</span>.Delete(path2);<br />}</pre><p>Of course, this code requires a reference to the COM type library - namely the "Microsoft Message Queue 3.0 Object Library" on the COM list in VS2008 when you have MSMQ installed on your dev box.</p><p>I had some weird problems trying to test this code on my machine, hence the commented machinename and formatname.  I think the problem was probably related more to the configuration of my machine than it was the code.  I suspect, however, that there may be some complexities that require you to specify machine name, queue name, and format name differently depending on whether you are working with a local queue or a remote one.</p><p>I found that for a local queue, the easy way to reference it was the code snippet above (don't specify machine, don't specify format name, supply the "path").  For a remote queue, I suspect that it will be easier to pass the machine name, the format name, and omit (pass null for) the path.  Note, the API states that you should NOT pass both the format name and the path name, or it will give an exception.</p><p>As always, if you have questions regarding this code, please don't hesitate to contact me via the comments or my email.</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-18928759185557505062008-11-01T10:24:00.000-07:002008-11-01T11:09:26.570-07:00.NET wish list...<ul><br /><li>Generics that can support 'text-like' replacement at runtime, where you can basically say that "I know that type T actually has a method called Foo with the signature bool Foo(int) but may not implement some particular interface (since you may not control the implementation of T)". I'd like to be able to call Foo from my generic class even if I can't change T to implement an interface that supports Foo. I'm thinking something like: <span style="font-family:courier new;"><blockquote><pre>public static void DoSomething<t>(T target)<br /> where T: class having bool Foo(int)<br />{<br /> if(target.Foo(0))<br /> Console.WriteLine("Woohoo");<br />}</span></pre></blockquote></li><li>A way of marking objects that MUST be used in a 'using' expression (i.e. they only make sense there) and having the C# compiler enforce it. For instance, an attribute would work for me (similar to how 'FlagsAttribute' indicates special semantics on enums). My reasoning for this is that I'd like to use IDisposable for some C++-style RAII-like stuff, but there's no way to guarantee that the objects are used correctly.</li><li>A way of injecting simple code / hooking "before" and "after" property notifications on 'automatic properties' in C#. For instance, if I do an automatic property, I'd love to be able to say "any time this changes, call this method", or something like that. It could be useful for INotifyPropertyChanged, but also for other things as well.</li></ul>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-61988485292662716752008-08-09T00:23:00.001-07:002008-08-09T00:31:07.995-07:00Building a WPF Grid Control (Part 2 of ?)<p>So, last time (<a title="Previous Post (KLeahy Technical)" href="http://kleahy-technical.blogspot.com/2008/08/building-wpf-grid-control-part-1-of.html" target="_blank">Building a WPF Grid Control (Part 1 of ?)</a>) we described some of the data binding structures of our WPF grid control. I'm not going to provide implementations of those interfaces yet, instead concentrating on some of the interesting bits of the control's implementation. For the most part, building the control comes down to several bits of functionality - scrolling, rendering, mouse support, keyboard support, and data binding. We've already seen the data binding part, so next I want to concentrate on scrolling.</p><p>Building scrolling into the UI is not terribly difficult, and for the most part, the implementation can be done without actually dealing with the data binding interfaces at all. However, there are a few details that we need from the interfaces in order to have a go at scrolling - namely, we need to know how big the fixed and scrolling regions need to be. Therefore, we need a dummy implementation of IDimensionMetrics that give us some sizes to play with. Since we currently won't have fixed rows or fixed columns, and all the grid cares about for laying out the regions is the TotalSpace member of this interface, we should be able to get away with a really dumb implementation.</p><p>So, let's implement IDimensionMetrics as:</p><pre class="code"><span style="color:blue;">public class </span><span style="color:#2b91af;">ReallyDumbMetrics </span>: <span style="color:#2b91af;">IDimensionMetrics<br /></span>{<br /> <span style="color:blue;">private double </span>_TotalSpace;<br /><br /> <span style="color:blue;">public </span>ReallyDumbMetrics(<span style="color:blue;">double </span>totalSpace)<br /> {<br /> _TotalSpace = totalSpace;<br /> }<br /><br /> <span style="color:blue;">#region </span>IDimensionMetrics Members<br /><br /> <span style="color:blue;">public double </span>GetSpace(<span style="color:blue;">int </span>index)<br /> {<br /> <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>();<br /> }<br /><br /> <span style="color:blue;">public void </span>SetSpace(<span style="color:blue;">int </span>index, <span style="color:blue;">double </span>space)<br /> {<br /> <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>();<br /> }<br /><br /> <span style="color:blue;">public double </span>GetStart(<span style="color:blue;">int </span>index)<br /> {<br /> <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>();<br /> }<br /><br /> <span style="color:blue;">public double </span>TotalSpace<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_TotalSpace; }<br /> }<br /><br /> <span style="color:blue;">public int </span>Count<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public event </span><span style="color:#2b91af;">EventHandler </span>SpaceChanged;<br /><br /> <span style="color:blue;">#endregion<br /></span>}</pre><p>I just realized that I forgot to describe the IGridBindings interface in my last post. This interface is very simple and is just a container for the various pieces of the bindings (selection info, dimensions, region render info) that allows a single point of binding for the grid control. The interface is defined as:</p><pre class="code"><span style="color:blue;">public interface </span><span style="color:#2b91af;">IGridBindings<br /></span>{<br /> <span style="color:#2b91af;">IRegionRenderInfo </span>TopLeftRenderInfo { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IRegionRenderInfo </span>HScrollRenderInfo { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IRegionRenderInfo </span>VScrollRenderInfo { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IRegionRenderInfo </span>HVScrollRenderInfo { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:#2b91af;">IDimensionMetrics </span>FixedRowMetrics { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IDimensionMetrics </span>FixedColMetrics { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IDimensionMetrics </span>ScrollingRowMetrics { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IDimensionMetrics </span>ScrollingColMetrics { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:#2b91af;">ISelectionInfo </span>SelectionInfo { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">void </span>Reorder(<span style="color:blue;">int</span>[] oldPositions);<br />}</pre><p>Since we'll be starting on our grid control now, it would be nice to have a IGridBindings implementation that will allow us to start working with the binding interfaces of the grid. So, let's go with a dummy implementation of GridBindings that uses our ReallyDumbMetrics implementation above. Here goes:</p><pre class="code"><span style="color:blue;">public class </span><span style="color:#2b91af;">ReallyDumbGridBindings </span>: <span style="color:#2b91af;">IGridBindings<br /></span>{<br /> <span style="color:#2b91af;">ReallyDumbMetrics </span>_FixedRowMetrics;<br /> <span style="color:#2b91af;">ReallyDumbMetrics </span>_FixedColMetrics;<br /> <span style="color:#2b91af;">ReallyDumbMetrics </span>_ScrollingRowMetrics;<br /> <span style="color:#2b91af;">ReallyDumbMetrics </span>_ScrollingColMetrics;<br /><br /> <span style="color:blue;">public </span>ReallyDumbGridBindings(<span style="color:blue;">double </span>fixedRowSize, <span style="color:blue;">double </span>fixedColSize, <span style="color:blue;">double </span>scrollRowSize, <span style="color:blue;">double </span>scrollColSize)<br /> {<br /> _FixedRowMetrics = <span style="color:blue;">new </span><span style="color:#2b91af;">ReallyDumbMetrics</span>(fixedRowSize);<br /> _FixedColMetrics = <span style="color:blue;">new </span><span style="color:#2b91af;">ReallyDumbMetrics</span>(fixedColSize);<br /> _ScrollingRowMetrics = <span style="color:blue;">new </span><span style="color:#2b91af;">ReallyDumbMetrics</span>(scrollRowSize);<br /> _ScrollingColMetrics = <span style="color:blue;">new </span><span style="color:#2b91af;">ReallyDumbMetrics</span>(scrollColSize);<br /> }<br /><br /> <span style="color:blue;">#region </span>IGridBindings Members<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IRegionRenderInfo </span>TopLeftRenderInfo<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IRegionRenderInfo </span>HScrollRenderInfo<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IRegionRenderInfo </span>VScrollRenderInfo<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IRegionRenderInfo </span>HVScrollRenderInfo<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IDimensionMetrics </span>FixedRowMetrics<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_FixedRowMetrics; }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IDimensionMetrics </span>FixedColMetrics<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_FixedColMetrics; }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IDimensionMetrics </span>ScrollingRowMetrics<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_ScrollingRowMetrics; }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IDimensionMetrics </span>ScrollingColMetrics<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_ScrollingColMetrics; }<br /> }<br /><br /> <span style="color:blue;">public </span><span style="color:#2b91af;">ISelectionInfo </span>SelectionInfo<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>(); }<br /> }<br /><br /> <span style="color:blue;">public void </span>Reorder(<span style="color:blue;">int</span>[] oldPositions)<br /> {<br /> <span style="color:blue;">throw new </span><span style="color:#2b91af;">NotImplementedException</span>();<br /> }<br /><br /> <span style="color:blue;">#endregion<br /></span>}</pre><p>Ok... I think we're ready to write some Grid code...</p><h2>Region Implementation</h2><p>Let's start with a simple implementation of a GridRegion base class that will provide the functionality for the four cell-containing regions in the grid UI. This class will derive from FrameworkElement just as our Grid does, and will participate in the layout system as usual (measure and arrange passes). For now, to avoid getting bogged down in the details of rendering rows and columns, we'll make it just draw big red ellipses in the regions.</p><p>Let's start with the GridRegion. We can begin with the following class:</p><pre class="code"><span style="color:blue;">internal class </span><span style="color:#2b91af;">GridRegion</span>: <span style="color:#2b91af;">FrameworkElement<br /></span>{<br /> <span style="color:blue;">public </span>GridRegion()<br /> {<br /> }<br />}</pre><p>Now, for any FrameworkElement, we need to support Measure and Arrange layout passes. We also need to support the dimension metrics in order to obtain the sizes for our control. The dimension metrics additions to GridRegion will be:</p><pre class="code"><span style="color:blue;">private </span><span style="color:#2b91af;">IDimensionMetrics </span>_RowMetrics;<br /><span style="color:blue;">private </span><span style="color:#2b91af;">IDimensionMetrics </span>_ColMetrics;<br /><br /><span style="color:blue;">private void </span>_ReplaceMetrics(<span style="color:blue;">ref </span><span style="color:#2b91af;">IDimensionMetrics </span>target, <span style="color:#2b91af;">IDimensionMetrics </span>source)<br />{<br /> <span style="color:blue;">if </span>(target != <span style="color:blue;">null</span>)<br /> target.SpaceChanged -= Metrics_SpaceChanged;<br /> target = source;<br /> <span style="color:blue;">if </span>(target != <span style="color:blue;">null</span>)<br /> target.SpaceChanged += Metrics_SpaceChanged;<br />}<br /><br /><span style="color:blue;">public void </span>SetBindings(<span style="color:#2b91af;">IDimensionMetrics </span>rowMetrics, <span style="color:#2b91af;">IDimensionMetrics </span>colMetrics)<br />{<br /> _ReplaceMetrics(<span style="color:blue;">ref </span>_RowMetrics, rowMetrics);<br /> _ReplaceMetrics(<span style="color:blue;">ref </span>_ColMetrics, colMetrics);<br /> InvalidateMeasure();<br /> InvalidateVisual();<br />}<br /><br /><span style="color:blue;">void </span>Metrics_SpaceChanged(<span style="color:blue;">object </span>sender, <span style="color:#2b91af;">EventArgs </span>e)<br />{<br /> InvalidateMeasure();<br /> InvalidateVisual();<br />}</pre><p>The basic idea here is the SetBindings method, used by the EditorGrid (that owns the GridRegion objects) to initialize the RowMetrics and ColMetrics properties. Each time the Bindings property of the EditorGrid is changed (it's the property that holds the IGridBindings interface reference) the grid will call SetBindings on each of the four regions.</p><p>We also need to support the layout and arrange passes for our control. Our container (the grid) will decide the layout of our control, all we need to do is request as much space as it is willing to give us (by returning availableSize from the MeasureOverride method as follows).</p><pre class="code"><span style="color:blue;">protected override </span><span style="color:#2b91af;">Size </span>MeasureOverride(<span style="color:#2b91af;">Size </span>availableSize)<br />{<br /> <span style="color:blue;">return </span>availableSize;<br />}</pre><p>This same functionality could possibly be achieved in another way, but this was the easiest way that I found. I suspect that setting the alignment properties to 'stretch' might have worked, but it didn't seem to (or at least I don't remember it working when I thought I tried it).</p><h2>Grid Implementation</h2><p>Ok, we can now start implementing the grid control itself. Let's start with this class, similar to the GridRegion we just completed:</p><pre class="code"><span style="color:blue;">public class </span><span style="color:#2b91af;">EditorGrid</span>: <span style="color:#2b91af;">FrameworkElement<br /></span>{<br /> <span style="color:#2b91af;">GridRegion </span>_TopLeftNonScroll;<br /> <span style="color:#2b91af;">GridRegion </span>_HScrollRegion;<br /> <span style="color:#2b91af;">GridRegion </span>_VScrollRegion;<br /> <span style="color:#2b91af;">GridRegion </span>_HVScrollRegion;<br /><br /> <span style="color:blue;">public </span>EditorGrid()<br /> {<br /> _TopLeftNonScroll = <span style="color:blue;">new </span><span style="color:#2b91af;">GridRegion</span>();<br /> _HScrollRegion = <span style="color:blue;">new </span><span style="color:#2b91af;">GridRegion</span>();<br /> _VScrollRegion = <span style="color:blue;">new </span><span style="color:#2b91af;">GridRegion</span>();<br /> _HVScrollRegion = <span style="color:blue;">new </span><span style="color:#2b91af;">GridRegion</span>();<br /> }<br /><br /> <span style="color:blue;">private </span><span style="color:#2b91af;">IGridBindings </span>_Bindings;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">IGridBindings </span>Bindings<br /> {<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_Bindings; }<br /> <span style="color:blue;">set<br /> </span>{<br /> <span style="color:blue;">if </span>(_Bindings != <span style="color:blue;">value</span>)<br /> {<br /> <span style="color:blue;">if </span>(_Bindings != <span style="color:blue;">null</span>)<br /> {<br /> _Bindings.FixedColMetrics.SpaceChanged -= Metrics_SpaceChanged;<br /> _Bindings.FixedRowMetrics.SpaceChanged -= Metrics_SpaceChanged;<br /> _Bindings.ScrollingColMetrics.SpaceChanged -= Metrics_SpaceChanged;<br /> _Bindings.ScrollingRowMetrics.SpaceChanged -= Metrics_SpaceChanged;<br /> }<br /> _Bindings = <span style="color:blue;">value</span>;<br /> <span style="color:blue;">if </span>(_Bindings != <span style="color:blue;">null</span>)<br /> {<br /> _Bindings.FixedColMetrics.SpaceChanged += Metrics_SpaceChanged;<br /> _Bindings.FixedRowMetrics.SpaceChanged += Metrics_SpaceChanged;<br /> _Bindings.ScrollingColMetrics.SpaceChanged += Metrics_SpaceChanged;<br /> _Bindings.ScrollingRowMetrics.SpaceChanged += Metrics_SpaceChanged;<br /> }<br /> _TopLeftNonScroll.SetBindings(_Bindings.FixedRowMetrics, _Bindings.FixedColMetrics);<br /> _HScrollRegion.SetBindings(_Bindings.FixedRowMetrics, _Bindings.ScrollingColMetrics);<br /> _VScrollRegion.SetBindings(_Bindings.ScrollingRowMetrics, _Bindings.FixedColMetrics);<br /> _HVScrollRegion.SetBindings(_Bindings.ScrollingRowMetrics, _Bindings.ScrollingColMetrics);<br /><br /> InvalidateMeasure();<br /> InvalidateVisual();<br /> }<br /> }<br /> }<br /><br /> <span style="color:blue;">void </span>Metrics_SpaceChanged(<span style="color:blue;">object </span>sender, <span style="color:#2b91af;">EventArgs </span>e)<br /> {<br /> InvalidateMeasure();<br /> InvalidateVisual();<br /> }<br />}</pre><p>As you can see, the bindings on the regions are set to different metrics depending on their locations in the grid. The TopLeftNonScroll region uses 'Fixed' metrics for both rows and columns, the HScrollRegion uses 'Scrolling' for columns and 'Fixed' for rows, and so on.</p><p>We now need to discuss the layout of the children of the editor grid. We also need to add scrollbars and the other non-cellular regions. For the moment, we'll ignore the other regions and the scrollbars. Let's just get the cellular regions in place first. In order to do all the layout stuff, we'll create a helper class that will make things easier for us. I'll call this class LayoutMetrics and define it as follows:</p><pre class="code"><span style="color:blue;">internal class </span><span style="color:#2b91af;">LayoutMetrics<br /></span>{<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>vscroll_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>hscroll_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>hscrollR_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>vscrollR_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>hvscrollR_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>topleft_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>topright_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>botleft_rect;<br /> <span style="color:blue;">public </span><span style="color:#2b91af;">Rect </span>botright_rect;<br /><br /> <span style="color:blue;">public </span>LayoutMetrics(<span style="color:#2b91af;">Size </span>size, <span style="color:blue;">double </span>vscroll_width, <span style="color:blue;">double </span>hscroll_height, <span style="color:blue;">double </span>fixedRowHeight, <span style="color:blue;">double </span>fixedColWidth)<br /> {<br /> vscroll_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(size.Width - vscroll_width,<br /> fixedRowHeight,<br /> vscroll_width,<br /> <span style="color:#2b91af;">Math</span>.Max(0, size.Height - hscroll_height - fixedRowHeight));<br /><br /> hscroll_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(fixedColWidth,<br /> size.Height - hscroll_height,<br /> <span style="color:#2b91af;">Math</span>.Max(0, size.Width - vscroll_width - fixedColWidth),<br /> hscroll_height);<br /><br /> hscrollR_rect = hscroll_rect;<br /> hscrollR_rect.Y = 0;<br /> hscrollR_rect.Height = fixedRowHeight + 1;<br /><br /> vscrollR_rect = vscroll_rect;<br /> vscrollR_rect.X = 0;<br /> vscrollR_rect.Width = fixedColWidth + 1;<br /><br /> hvscrollR_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(hscroll_rect.X, vscroll_rect.Y,<br /> hscroll_rect.Width, vscroll_rect.Height);<br /><br /> topleft_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(<span style="color:blue;">new </span><span style="color:#2b91af;">Size</span>(fixedColWidth + 1, fixedRowHeight + 1));<br /> topright_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(vscroll_rect.X, 0, vscroll_rect.Width, hscrollR_rect.Height - 1);<br /> botleft_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(0, hscroll_rect.Y, vscrollR_rect.Width - 1, hscroll_rect.Height);<br /> botright_rect =<br /> <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(vscroll_rect.X, hscroll_rect.Y, vscroll_rect.Width, hscroll_rect.Height);<br /> }<br />}</pre><p>The main idea of this class is to break the space occupied by the Grid into the component rectangles. For now, we'll supply some dummy values for the sizes of the horizontal and vertical scrollbars. Now, given this class, we can implement our 'measure' and 'arrange' methods for the WPF layout system. We do so (on our EditorGrid class) as follows:</p><pre class="code"><span style="color:blue;">protected override </span><span style="color:#2b91af;">Size </span>MeasureOverride(<span style="color:#2b91af;">Size </span>availableSize)<br />{<br /> <span style="color:green;">// for now, fake the sizes of the scroll bars just to reserve some space.<br /> </span><span style="color:#2b91af;">LayoutMetrics </span>m = <span style="color:blue;">new </span><span style="color:#2b91af;">LayoutMetrics</span>(availableSize,<br /> 14, <span style="color:green;">// vscroll_width<br /> </span>14, <span style="color:green;">// hscroll_width<br /> </span>_Bindings.FixedRowMetrics.TotalSpace,<br /> _Bindings.FixedColMetrics.TotalSpace);<br /><br /> _TopLeftNonScroll.Measure(m.topleft_rect.Size);<br /> _HScrollRegion.Measure(m.hscrollR_rect.Size);<br /> _VScrollRegion.Measure(m.vscrollR_rect.Size);<br /> _HVScrollRegion.Measure(m.hvscrollR_rect.Size);<br /><br /> <span style="color:blue;">return </span>availableSize;<br />}<br /><br /><span style="color:blue;">protected override </span><span style="color:#2b91af;">Size </span>ArrangeOverride(<span style="color:#2b91af;">Size </span>finalSize)<br />{<br /> <span style="color:green;">// for now, fake the sizes of the scroll bars just to reserve some space.<br /> </span><span style="color:#2b91af;">LayoutMetrics </span>m = <span style="color:blue;">new </span><span style="color:#2b91af;">LayoutMetrics</span>(finalSize,<br /> 14, <span style="color:green;">// vscroll_width<br /> </span>14, <span style="color:green;">// hscroll_width<br /> </span>_Bindings.FixedRowMetrics.TotalSpace,<br /> _Bindings.FixedColMetrics.TotalSpace);<br /><br /> _TopLeftNonScroll.Arrange(m.topleft_rect);<br /> _HScrollRegion.Arrange(m.hscrollR_rect);<br /> _VScrollRegion.Arrange(m.vscrollR_rect);<br /> _HVScrollRegion.Arrange(m.hvscrollR_rect);<br /><br /> <span style="color:blue;">return </span>finalSize;<br />}</pre><p>Now, we need to add visual tree support to our grid control. In order to do this, we need a few features - first, we need to add the regions to the visual tree by calling AddVisualChild on our grid visual. Second, we need to override the 'render list' method & property GetVisualChild and VisualChildrenCount respectively. The first (calling AddVisualChild) we do by adding the following lines to the constructor (after the creation of the regions):</p><pre class="code">AddVisualChild(_TopLeftNonScroll);<br />AddVisualChild(_HScrollRegion);<br />AddVisualChild(_VScrollRegion);<br />AddVisualChild(_HVScrollRegion);</pre><p>Now that we have those lines in the constructor, we need to implement the 'rendering' functionality. The easiest way to do this is either with a VisualCollection, or since in our case we have a fixed list, just an array of Visuals. The GetVisualChild method must return visuals in the order in which they should be rendered, and we want our regions to render in the following order: HVScrollRegion, HScrollRegion, VScrollRegion, TopLeftNonScroll. We will add a field to EditorGrid class that is an array of visuals (Visual[]) called _Visuals, and initialize it in the constructor (after the four lines above) as follows:</p><pre class="code">_Visuals = <span style="color:blue;">new </span><span style="color:#2b91af;">Visual</span>[]<br />{<br /> _HVScrollRegion,<br /> _HScrollRegion,<br /> _VScrollRegion,<br /> _TopLeftNonScroll<br />};</pre><p>Additionally, we need to implement the GetVisualChild method and VisualChildrenCount property as follows:</p><pre class="code"><span style="color:blue;">protected override </span><span style="color:#2b91af;">Visual </span>GetVisualChild(<span style="color:blue;">int </span>index)<br />{<br /> <span style="color:blue;">return </span>_Visuals[index];<br />}<br /><br /><span style="color:blue;">protected override int </span>VisualChildrenCount<br />{<br /> <span style="color:blue;">get </span>{ <span style="color:blue;">return </span>_Visuals.Length; }<br />}</pre><p>We now need to implement rendering in our GridRegion and then we'll have something we can start messing with. Here's the implementation of the OnRender method for the GridRegion control.</p><pre class="code"><span style="color:blue;">protected override void </span>OnRender(<span style="color:#2b91af;">DrawingContext </span>drawingContext)<br />{<br /> <span style="color:blue;">if </span>(_RowMetrics == <span style="color:blue;">null </span> _ColMetrics == <span style="color:blue;">null</span>)<br /> <span style="color:blue;">return</span>;<br /><br /> <span style="color:blue;">double </span>xmid = _ColMetrics.TotalSpace / 2;<br /> <span style="color:blue;">double </span>ymid = _RowMetrics.TotalSpace / 2;<br /><br /> drawingContext.PushClip(<span style="color:blue;">new </span><span style="color:#2b91af;">RectangleGeometry</span>(<span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(RenderSize)));<br /> drawingContext.DrawEllipse(<span style="color:#2b91af;">Brushes</span>.Red, <span style="color:blue;">null</span>, <span style="color:blue;">new </span><span style="color:#2b91af;">Point</span>(xmid, ymid), xmid, ymid);<br /> drawingContext.Pop();<br />}</pre><p>Next time, we'll work on getting some scrolling features working.</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com2tag:blogger.com,1999:blog-8594690550975135798.post-29829906696624069932008-08-01T13:21:00.001-07:002008-08-09T00:33:36.073-07:00Building a WPF Grid Control (Part 1 of ?)<p>So, some of you may know that I'm building a Grid control for our MG-ALFA application. We started by looking at the WPF grids out there, and found that none of them really fit our needs. There were several issues with the controls on the market. Much of what we wanted was very 'simple', as far as look & feel (like a traditional grid), yet all the WPF grids on the market seemed to be focused on 'pretty'. Also, we needed to be able to customize several very specific features - for instance, we wanted dragging of columns for reordering, fixed rows & columns, and the ability to easily transpose the grid.</p><p>None of these features were easy to come by in existing controls, and in order to get an of them, we would have to heavily customize the controls out of the box. The customization would be 'on top' of the control, so there wasn't a good way to tie it to our data model, and transpose was the killer feature. In order to get transpose, we would have had to write some really nasty code and do some really unpleasant things with databinding. If we didn't do those nasty things, we'd have to use 'unbound' mode on the controls, which would lead to really unpleasant code to keep the grid in sync with the data changes.</p><p>Finally, after a ton of investigation, we decided we'd be better off just writing our own control and building a truly custom data model for the grid, rather than trying to force fit an existing control to our problem. We were very skeptical about the amount of time it would take to build a grid that had the features we needed, but I was pretty sure it would be less than a few weeks, and it turns out I was mostly right about that. I'm going to try to describe the design of the control, from the ground up, in a series of blog posts, but I hope you'll ask questions if you want more details, as I undoubtedly won't cover everything.</p><p>So, first, let's talk about the basic design of the grid, and the features we required. The grid is built on WPF mostly using visual layer programming to do the rendering. It supports fixed rows, fixed columns, and has row and column headings. Visually it looks very much like MS Excel. The UX is also intended to be very much like Excel, except with some modifications that are specific to our domain needs (and no support for formulas).</p><h2>What to derive from?</h2><p>The first decision in any custom control development for WPF is to decide which of the multitude of classes you should derive from. The class hierarchy is:</p><blockquote><p>Visual<br /> UIElement<br /> FrameworkElement<br /> Control<br /> ...</p></blockquote><p>Visual is very rudimentary, and basically provides only the ability to manage and participate in the visual tree. It is possible to build components at the 'Visual' level, but relatively difficult, and they must be built from other components that are at a higher level (at least UIElement). This is because the only thing a Visual can effectively do is contain other Visuals, and perform hit testing. There are a few Visual-derived classes that can be used by control designers (DrawingVisual, ContainerVisual), but these aren't really useful as base classes (maybe ContainerVisual, but certainly not DrawingVisual).</p><p>UIElement is effectively the lowest level class that a control designer might want to derive from. It provides basic layout, event handling, focus support, and rendering features. To provide code for rendering a UIElement, you must override OnRender. You also will want to override MeasureCore and ArrangeCore to participate in the layout process. You may also want to override HitTestCore to provide sophisticated hit testing for your control, especially if you have a non-rectangular area (our control will be rectangular and covered by other child controls, so we don't really need to mess with HitTestCore).</p><p>FrameworkElement is really the 'entry point' into WPF framework-level programming. Much of the core functionality for rendering is introduced in the UIElement class, but FrameworkElement builds on these features and provides some core implementation that makes it easier for you to implement the layout methods (i.e. it handles things like HorizontalAlignment, VerticalAlignment, Width, Height, etc., so you don't have to write the tedious code to make these work in your MeasureCore and ArrangeCore implementations). It also provides the core functionality needed for data binding. We'll derive our grid control from FrameworkElement, because it's the lowest class we can derive from without making a ton of extra work for ourselves, and it's the highest class we can effectively derive from without introducing features we don't want.</p><p>Control introduces style and templating support. Since we specifically don't want the XAML user to be able to customize the control template and styles for our grid (we want very specific control over how things are rendered), and don't need the flexibility that styles and templates provide, we don't want Control. However, it should be noted that if you want to build the 'best' control, from a flexibility standpoint, you probably do want to provide these features and use the 'recommended' approach of deriving from something at the Control or higher levels of the inheritance tree.</p><h2>Visual Layout</h2><p>Our grid control looks like the screenshot below (currently). It is still a work in progress, and that's why those ugly orange sections are there, and why the fixed rows look kinda funny (no gridlines, green background, etc.).</p><p><a href="http://lh3.ggpht.com/kelly.p.leahy/SJNwQM07xgI/AAAAAAAAABE/_mR_DEDwWQk/image%5B6%5D.png"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height="400" alt="image" src="http://lh6.ggpht.com/kelly.p.leahy/SJNwQtHUAuI/AAAAAAAAABM/P8NMwa_Dphg/image_thumb%5B4%5D.png" width="508" border="0" /></a> </p><p>Obviously the data I've been working with is dummy data, generated by my data source for my benefit during development.</p><p>My first step in building the control was to design the layout of the control, in terms of separate 'regions' of the grid, based on their scrolling nature, and based on the relative positions of the scrolling regions. In the picture below, I've labeled the 9 independent regions of the grid control.</p><p><a href="http://lh5.ggpht.com/kelly.p.leahy/SJNwRMFR_AI/AAAAAAAAABQ/-xfJd98KNOU/image%5B18%5D.png"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height="378" alt="image" src="http://lh4.ggpht.com/kelly.p.leahy/SJNwRcZ7DQI/AAAAAAAAABU/RXaOItDteb4/image_thumb%5B12%5D.png" width="512" border="0" /></a> </p><p>The regions, in left-to-right (top-to-bottom) order are:</p><ul><li>TopLeftNonScroll - the fixed row/fixed column intersection, including the "select all" box.</li><li>HScrollRegion - the horizontal-only scrolling region (fixed rows, scrolling columns).</li><li>TopRightNonScroll - the area above the vertical scroll bar, that doesn't scroll and will eventually house buttons or some other visual cue / support.</li><li>VScrollRegion - the vertical-only scrolling region (fixed columns, scrolling rows).</li><li>HVScrollRegion - the 'data' section of the grid. This area scrolls both directions, and is made up of the scrolling rows / scrolling columns intersection.</li><li>VScrollBar - the vertical scroll bar (a ScrollBar control with its Orientation set to Orientation.Vertical)</li><li>BottomLeftNonScroll - the non scrolling area to the left of the horizontal scroll bar, will eventually be another place for buttons, etc.</li><li>HScrollBar - the horizontal scroll bar (a ScrollBar control with its Orientation set to Orientation.Horizontal).</li><li>BottomRightNonScroll - this will likely just be a gray 'dead-zone' so the scroll bars don't look stupid.</li></ul><p>For the most part, except for scrolling, the HScrollRegion, TopLeftNonScroll, VScrollRegion, and HVScrollRegion have the same UI / UX, so I've combined much of the functionality into a single base class called "GridRegion". It has some parameterized options (like which directions it can scroll), but for the most part the code is all shared and used from this class. The derived classes are generally pretty small, and are just responsible for 'customizing' the GridRegion functionality.</p><p>The other regions are currently implemented just as canvases (except the ScrollBars, of course).</p><h2>Data Binding model</h2><p>My data binding model has several parts. The entire model is really a 'view' from the standpoint of MVP-like design patterns (at least in my assessment it is). The real data model is specific to the application. The Data Binding model supported by the grid has bindings for the selection and active / anchor cells, the row & column sizes, and the contents of the cells (including render flags and other special items).</p><p>In order to simplify the design and implementation, I've separated the binding into several objects based on the way the regions break up the grid. The major breakdown is between dimension metrics, render info, and selection support.</p><h3>Dimension Metrics (sizes of rows / columns)</h3><p>For the dimensions of the rows and columns, I've defined an interface called IDimensionMetrics that allows management of and provides information about the sizes of rows or columns. It is defined as follows.</p><pre class="code"><span style="color:blue;">public interface </span><span style="color:#2b91af;">IDimensionMetrics<br /></span>{<br /> <span style="color:blue;">double </span>GetSpace(<span style="color:blue;">int </span>index);<br /> <span style="color:blue;">void </span>SetSpace(<span style="color:blue;">int </span>index, <span style="color:blue;">double </span>space);<br /> <span style="color:blue;">double </span>GetStart(<span style="color:blue;">int </span>index);<br /> <span style="color:blue;">double </span>TotalSpace { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>Count { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">event </span><span style="color:#2b91af;">EventHandler </span>SpaceChanged;<br />}</pre><p>Each object that implements IDimensionMetrics only represents a single group of rows or columns. There are 4 implementations of IDimensionMetrics for a single set of grid bindings. The columns are broken into "fixed" and "scrolling", as are the rows (for a total of 4 separate groups).</p><p>In order to provide some additional features for IDimensionMetrics without requiring that all implementers implement these features, I've used some extension methods to implement common algorithms based on IDimensionMetrics. The extension class is as follows.</p><pre class="code"><span style="color:blue;">internal static class </span><span style="color:#2b91af;">DimensionExtensions<br /></span>{<br /> <span style="color:blue;">public static double </span>GetEnd(<span style="color:blue;">this </span><span style="color:#2b91af;">IDimensionMetrics </span>metrics, <span style="color:blue;">int </span>index)<br /> {<br /> <span style="color:blue;">return </span>metrics.GetStart(index) + metrics.GetSpace(index);<br /> }<br /><br /> <span style="color:blue;">public static int </span>HitTestNoSizing(<span style="color:blue;">this </span><span style="color:#2b91af;">IDimensionMetrics </span>metrics, <span style="color:blue;">double </span>v)<br /> {<br /> <span style="color:blue;">for </span>(<span style="color:blue;">int </span>i = 0; i < metrics.Count; i++)<br /> {<br /> <span style="color:blue;">double </span>ofs = v - metrics.GetStart(i);<br /> <span style="color:blue;">if </span>(ofs >= 0 && ofs <= metrics.GetSpace(i))<br /> <span style="color:blue;">return </span>i;<br /> }<br /> <span style="color:blue;">return </span>-1;<br /> }<br /><br /> <span style="color:blue;">public static int </span>HitTestWithSizing(<span style="color:blue;">this </span><span style="color:#2b91af;">IDimensionMetrics </span>metrics, <span style="color:blue;">double </span>v, <span style="color:blue;">out bool </span>overSizingGrip)<br /> {<br /> <span style="color:blue;">for </span>(<span style="color:blue;">int </span>i = 0; i < metrics.Count; i++)<br /> {<br /> <span style="color:blue;">double </span>ofs = v - metrics.GetStart(i);<br /> <span style="color:blue;">double </span>space = metrics.GetSpace(i);<br /> <span style="color:blue;">if </span>(ofs.InNeighborhood(space, 3))<br /> {<br /> overSizingGrip = <span style="color:blue;">true</span>;<br /> <span style="color:blue;">return </span>i;<br /> }<br /> <span style="color:blue;">else if </span>(ofs.InBetween(0, space, <span style="color:#2b91af;">DoubleExtensions</span>.<span style="color:#2b91af;">EndpointInclusionMode</span>.LeftInclusive))<br /> {<br /> overSizingGrip = <span style="color:blue;">false</span>;<br /> <span style="color:blue;">return </span>i;<br /> }<br /> }<br /> overSizingGrip = <span style="color:blue;">false</span>;<br /> <span style="color:blue;">return </span>-1;<br /> }<br /><br /> <span style="color:blue;">private delegate </span><span style="color:#2b91af;">Rect CVR_GetRect</span>();<br /> <span style="color:blue;">private delegate void </span><span style="color:#2b91af;">CVR_UpdateRect</span>(<span style="color:blue;">double </span>space);<br /><br /> <span style="color:blue;">public static void </span>ComputeVisibleRange(<span style="color:blue;">this </span><span style="color:#2b91af;">IDimensionMetrics </span>metrics, <span style="color:#2b91af;">Rect </span>visibleRect, <span style="color:#2b91af;">Direction </span>direction, <span style="color:blue;">out int </span>first, <span style="color:blue;">out int </span>second)<br /> {<br /> <span style="color:blue;">if</span>(direction != <span style="color:#2b91af;">Direction</span>.Horizontal && direction != <span style="color:#2b91af;">Direction</span>.Vertical)<br /> <span style="color:blue;">throw new </span><span style="color:#2b91af;">ArgumentException</span>(<span style="color:#a31515;">"direction must be horizontal (columns) or vertical (rows)"</span>, <span style="color:#a31515;">"direction"</span>);<br /><br /> <span style="color:blue;">int </span>min = metrics.Count;<br /> <span style="color:blue;">int </span>max = -1;<br /><br /> <span style="color:blue;">var </span>initializeRect = direction == <span style="color:#2b91af;">Direction</span>.Vertical ?<br /> (<span style="color:#2b91af;">CVR_GetRect</span>)(() => <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(visibleRect.Left, 0, visibleRect.Width, 0))<br /> : (<span style="color:#2b91af;">CVR_GetRect</span>)(() => <span style="color:blue;">new </span><span style="color:#2b91af;">Rect</span>(0, visibleRect.Top, 0, visibleRect.Height));<br /><br /> <span style="color:#2b91af;">Rect </span>rngRect = initializeRect();<br /><br /> <span style="color:blue;">var </span>updateRectSize = direction == <span style="color:#2b91af;">Direction</span>.Vertical ?<br /> (<span style="color:#2b91af;">CVR_UpdateRect</span>)((<span style="color:blue;">double </span>space) => rngRect.Height = space)<br /> : (<span style="color:#2b91af;">CVR_UpdateRect</span>)((<span style="color:blue;">double </span>space) => rngRect.Width = space);<br /><br /> <span style="color:blue;">var </span>updateRectPos = direction == <span style="color:#2b91af;">Direction</span>.Vertical ?<br /> (<span style="color:#2b91af;">CVR_UpdateRect</span>)((<span style="color:blue;">double </span>space) => rngRect.Y += space)<br /> : (<span style="color:#2b91af;">CVR_UpdateRect</span>)((<span style="color:blue;">double </span>space) => rngRect.X += space);<br /><br /> <span style="color:blue;">for </span>(<span style="color:blue;">int </span>i = 0; i < metrics.Count; i++)<br /> {<br /> <span style="color:blue;">double </span>space = metrics.GetSpace(i);<br /> updateRectSize(space);<br /><br /> <span style="color:blue;">if </span>(rngRect.IntersectsWith(visibleRect))<br /> {<br /> min = <span style="color:#2b91af;">Math</span>.Min(i, min);<br /> max = <span style="color:#2b91af;">Math</span>.Max(i, max);<br /> }<br /><br /> updateRectPos(space);<br /> }<br /><br /> first = min;<br /> second = max;<br /> }<br />}</pre><p>In IDimensionMetrics, GetStart gives the starting position of a row or column, and GetSpace gives the space that it occupies. The extension method GetEnd returns the result of GetStart + GetSpace for a given column. TotalSpace is the sum of all GetSpace values for all rows/columns in the IDimensionMetrics. It could have also been computed as an extension method, but I decided it would be better as a property so that the IDimensionMetrics implementer could precompute it.</p><p>The HitTestNoSizing and HitTestWithSizing extension methods help determine which column or row a given point is over (for mouse hit testing). The former ignores sizing grips, while the latter will indicate the proper position for a sizing grip (currently hardcoded to a neighborhood of 3 device-independent pixels on each side of the sizing line).</p><h3>Selection Info</h3><p>First of all, my grid only supports selection of rows / columns / cells in the scrolling area of the grid. For that reason, I have a single SelectionInfo binding for all regions, and the columns / rows used by the SelectionInfo members (FirstCol, FirstRow, LastCol, LastRow, etc.) are relative to the top left corner of the data region (HVScrollRegion). If I needed support for selecting within the fixed rows and columns, then there would be some additional complexity in my code but it could be supported. I think of the fixed rows and fixed columns as essentially being 'extended' headers, so it doesn't make sense to select them, or have an active cell in these regions (just as it doesn't make sense to be able to make the active cell be the 'C' header in the C column of Excel!).</p><p>As with my other stuff, selection info has an interface that exposes the functionality required by the system - ISelectionInfo, defined as follows.</p><pre class="code"><span style="color:blue;">public enum </span><span style="color:#2b91af;">SelectionType<br /></span>{<br /> None,<br /> Rows,<br /> Columns,<br /> Cells,<br /> All,<br />}<br /><br /><span style="color:blue;">public interface </span><span style="color:#2b91af;">ISelectionInfo<br /></span>{<br /> <span style="color:#2b91af;">SelectionType </span>SelectionType { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>FirstRow { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>FirstCol { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>LastRow { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>LastCol { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">int </span>AnchorRow { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>AnchorCol { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">int </span>ActiveRow { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>ActiveCol { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">void </span>SelectColumn(<span style="color:blue;">int </span>column, <span style="color:blue;">bool </span>extend);<br /> <span style="color:blue;">void </span>SelectRow(<span style="color:blue;">int </span>row, <span style="color:blue;">bool </span>extend);<br /> <span style="color:blue;">void </span>SelectCell(<span style="color:blue;">int </span>row, <span style="color:blue;">int </span>column, <span style="color:blue;">bool </span>extend);<br /> <span style="color:blue;">void </span>SelectAll();<br /> <span style="color:blue;">void </span>Clear();<br /><br /> <span style="color:blue;">int </span>MaxRow { <span style="color:blue;">get</span>; }<br /> <span style="color:blue;">int </span>MaxCol { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">event </span><span style="color:#2b91af;">EventHandler </span>SelectionChanged;<br />}</pre><p>I'm still not sure whether this interface will remain the same forever, I might change it to act more like Excel (i.e. removing the distinction of SelectionType and just making the different selection rendering be handled by comparing FirstRow/FirstCol with 0, and LastRow/LastCol with MaxRow/MaxCol). The interface is pretty self-explanatory, except for AnchorXXX and ActiveXXX. ActiveXXX is used to track where the keyboard has you on a keyboard-based selection extension (i.e. you hold shift and move around with the keyboard). AnchorXXX is used to track the starting cell for the selection. When moving away from a selection (without the shift key held), AnchorXXX is the position from which you start. This is counterintuitive to me, but it's how Excel works so I've replicated it.</p><p>Notice that ISelectionInfo is just the 'keeper' of the selection, and provides some methods for modifying the selection, but it doesn't have anything to do with the keyboard or the mouse. The support for modifying the selection via keyboard or mouse is isolated in the KeyboardManager and MouseManager classes, discussed in a later post from this series.</p><h3>Render Info</h3><p>Within each of the regions, we need to be able to obtain and change the cell text, get the render flags (i.e. is it selected, is it a special cell, etc.), get the text alignment, and some other special info for the grid region. The interfaces involved are IRenderInfo and IButtonInfo. The applicable definitions are as follows.</p><pre class="code">[<span style="color:#2b91af;">Flags</span>]<br /><span style="color:blue;">public enum </span><span style="color:#2b91af;">CellRenderFlags<br /></span>{<br /> None = 0,<br /> TopLeft = 1,<br /> ColHeader = 2,<br /> RowHeader = 4,<br /> FixedCol = 8,<br /> FixedRow = 16,<br /> Active = 32,<br /> Selected = 64,<br /> Hover = 128,<br /> Anchor = 256,<br />}<br /><br /><span style="color:blue;">static class </span><span style="color:#2b91af;">CellRenderFlagsExtensions<br /></span>{<br /> <span style="color:blue;">public static bool </span>Contains(<span style="color:blue;">this </span><span style="color:#2b91af;">CellRenderFlags </span>target, <span style="color:#2b91af;">CellRenderFlags </span>flag)<br /> {<br /> <span style="color:blue;">return </span>(target & flag) == flag;<br /> }<br />}<br /><br /><br /><span style="color:blue;">public interface </span><span style="color:#2b91af;">IRegionRenderInfo<br /></span>{<br /><span style="color:green;"> </span><span style="color:#2b91af;">CellRenderFlags </span>GetCellFlags(<span style="color:blue;">int </span>row, <span style="color:blue;">int </span>col);<br /> <span style="color:blue;">string </span>GetCellText(<span style="color:blue;">int </span>row, <span style="color:blue;">int </span>col);<br /> <span style="color:blue;">void </span>SetCellText(<span style="color:blue;">int </span>row, <span style="color:blue;">int </span>col, <span style="color:blue;">string </span>text);<br /> <span style="color:#2b91af;">TextAlignment </span>GetTextAlignment(<span style="color:blue;">int </span>row, <span style="color:blue;">int </span>col);<br /> <br /> <span style="color:#2b91af;">Orientation </span>ButtonOrientation { <span style="color:blue;">get</span>; }<br /> <span style="color:#2b91af;">IButtonInfo </span>GetButtonInfo(<span style="color:blue;">int </span>index);<br /> <span style="color:blue;">int </span>ButtonCount { <span style="color:blue;">get</span>; }<br /><br /> <span style="color:blue;">event </span><span style="color:#2b91af;">EventHandler </span>ResetRenderInfo;<br />}<br /><br /><span style="color:blue;">public interface </span><span style="color:#2b91af;">IButtonInfo<br /></span>{<br /> ... <removed for brevity> ...<br />}</pre><p>We will ignore IButtonInfo and the associated bits from the IRenderInfo interface for now and focus on the other IRenderInfo information. The cell render flags are various pieces of info that allow the CellRenderer class (discussed in a later post) to do its work. The CellRenderer also needs to know the text alignment for the cell and the cell text itself. The SetCellText method is provided for the in-place editor that allows modification of the cell text.</p><p>This concludes our discussion of the data binding interfaces used by the grid.</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-49318859481192710562008-06-12T11:24:00.001-07:002008-06-12T11:25:42.269-07:00MSDN Articles Online (.chm)<p>Glenn Block (@gblock on Twitter) just posted an tweet that mentioned the existence of these.  I had no idea they were there and I'm sure others didn't as well - so here you go: <a href="http://is.gd/vZE">http://is.gd/vZE</a></p> <p>Enjoy!</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-8857536018743569182008-06-08T00:16:00.001-07:002008-06-08T00:20:32.964-07:00Extension Methods are way cool!<p>OK.  So everyone's heard of LINQ by now.  Most everyone has even heard of some of the cool features of C# 3.0 (lambdas), but in my mind, the coolest - extension methods - largely goes unnoticed.  Extension methods are the plumbing on which LINQ and some of the other cool features in the C# 3.0 libraries are implemented.  They are, in my opinion, the best feature C# has introduced since Generics, and are possibly one of the best features added to traditional languages EVER!</p><p>Consider the following - you have a class that someone else wrote.  On their class, they've provided a public interface for doing all of the things you need, but there are several additional things that you've implemented (as a separate utility set of functions) that it would be nice to add to the class' public interface.  Unfortunately, the class is marked 'sealed', or it is the base of a large hierarchy of classes that you simply can't add your functionality to (since you can't cause classes in a vendors library to derive from your 'new' version of their base class).</p><p>Extension methods to the rescue - all you need to do is declare a static class in your library (which you probably already have called 'StringUtils' or something like that :)), and provide some static methods on it that use the new 'this' keyword on their first argument.  Magically, the compiler will then 'add' this method to all items that have a type that is compatible with the type you have in the 'this-marked' argument.</p><p>For example:</p><pre>public static class StringUtils<br />{<br /> public static string RemoveAll(this string s, params string[] args)<br /> {<br /> string ret = s;<br /> foreach(string sremove in args)<br /> ret = ret.Replace(sremove, string.Empty);<br /> }<br />}</pre><p>by the way, of course I know this is the most horrible way to implement this function - it's just an example so don't tell me how crappy my code is or that I should be using StringBuilder or yada yada yada...!</p><p>The point of this is that after declaring such a function, all objects of type 'string' syntactically receive a member called 'RemoveAll' that has a single 'params' argument.  This is VERY cool.</p><p>The coolest thing about this - you can also do it for interfaces, enums, and various other types that you can't possibly provide "code" for in a more traditional way.</p><h4>What else?</h4><p>Much of the code that I write on a day-to-day basis works with tree-based data structures.  Some of these structures can get very complicated and much of my unit test code needs to do asserts over a large part of a tree (after performing some complex operation).  As a for instance, consider an expression parser.  Such a parser would presumably build an AST for the expression it's given and return that AST for further processing.  ASTs for all but the most simple expressions can get very tedious to 'check' for validity when writing a parser.</p><p>I've recently begun using extension methods for by base node class to help with my unit testing.  I directly put the unit test 'asserts' into the extension methods, and these extension methods are in NO WAY suitable to exist in the library that is being tested (why on earth would I want to have all this extra junk in my library just to support unit tests).  As a matter of fact, my libraries even target .NET 3.0 (C# 2.0) rather than .NET 3.5, C# 3.0.  However, that doesn't stop me from being able to use extension methods in my unit testing code (which doesn't get deployed to my clients, so I don't require them to have 3.5, I just have to have it on my dev machine and build machine).</p><p>Here's a simple example of how some of my unit testing code looks:</p><pre>[Test]<br />public void FormulaTests2()<br />{<br /> PrimaryLexer l = new PrimaryLexer();<br /> StringReaderAdapter sra = new StringReaderAdapter("a / (b + c)", 0);<br /> InforceScriptLexerFilter lf = new InforceScriptLexerFilter(sra, l);<br /> InforceScriptSemanticParser sp = new InforceScriptSemanticParser(lf);<br /><br /> RootFormula rf = sp.Parse();<br /><br /> // look for 'a' and '/'<br /> rf.Body.Is<binaryop>()<br /> .OperatorIs(InforceScriptTokenId.Slash)<br /> .Left.Is<invokeop>()<br /> .IdRef.Is<idreference>()<br /> .NameIs("a");<br /> // look for 'b' and '+'<br /> rf.Body.Is<binaryop>()<br /> .Right.Is<binaryop>()<br /> .OperatorIs(InforceScriptTokenId.Plus)<br /> .Left.Is<invokeop>()<br /> .IdRef.Is<idreference>()<br /> .NameIs("b");<br /> // look for 'c'<br /> rf.Body.Is<binaryop>()<br /> .Right.Is<binaryop>()<br /> .Right.Is<invokeop>()<br /> .IdRef.Is<idreference>()<br /> .NameIs("c");<br />}</pre><p>In order to make all this possible, I defined a few extension methods:</p><pre>internal static class TreeAssertions<br />{<br /> public static T Is<T>(this ExpressionBase node) where T : ExpressionBase<br /> {<br /> Assert.IsInstanceOfType(typeof(T), node, "wrong node type");<br /> return (T)node;<br /> }<br /><br /> public static IdReference NameIs(this IdReference node, string name)<br /> {<br /> Assert.AreEqual(name, node.Id.Name, "names don't match");<br /> return node;<br /> }<br /><br /> public static BinaryOp OperatorIs(this BinaryOp node, InforceScriptTokenId op)<br /> {<br /> Assert.AreEqual(op, node.OperatorTokenId);<br /> return node;<br /> }<br />}</pre><p>As you can see, the 'Is' test checks the type of a node, and then returns the node, so I can continue checking other things for the same node (assuming it 'passed' the check).  The same is true for the 'NameIs' and 'OperatorIs' checks.  This sort of programming is generally referred to (I think) as 'Literate Programming' - a technique for which the venerable D. Knuth is given the credit.  However, in order to do this sort of thing in the past, I'd have needed to put all these methods on my base class for the tree nodes, something that would have absolutely been the 'wrong' thing to do (since this test code should not be part of the library-proper).  (By the way, I think this style is now being referred to as 'fluent interfaces' in programming circles).</p><p>I can't wait to see what else I can find to use these methods for.  I've already found it to be an amazing benefit to my productivity and the readability of my tests.</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-14715807980457937012008-06-05T21:00:00.001-07:002008-06-05T21:00:09.944-07:00CI / TeamCity is Seriously COOL!<p>OK... I'll be the first to admit that I'm just getting into Agile processes and I'm still a bit skeptical.  At first, I thought (CI = Continuous Integration) 'CI builds - is it really worthwhile?'.  Now, I've got a <a href="http://www.jetbrains.com/teamcity/" target="_blank">TeamCity</a> site & build agent up and going, and I'm totally SOLD!</p> <p>Here are the benefits as I see them for our situation:</p> <ol> <li>We know almost immediately when someone broke the build (they know too!)</li> <li>We have better checkin quality now that people are tired of getting those 'compilation failed' emails.</li> <li>We always have a source to go to for a 'current' build - no need to get the sources and build on your own machine, or go ask a 'build master' to get you a build.</li> <li>We have other 'automation' points that we can hook into when we're ready to move on to bigger & better methods.</li> </ol> <p>As an example of #4, I hope to soon have our NUnit tests running as part of an automated build.  I also think we can have automated installer builds going if we wanted to.  And, best of all, by virtue of TeamCity's ability to 'watch' our source control server for updates, and it's ability to run any arbitrary command line, NAnt, or MSBuild (or many more) task in response to those updates, the sky is the limit!</p> <p>I can't wait to get more 'good stuff' implemented on TeamCity.</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com1tag:blogger.com,1999:blog-8594690550975135798.post-72126111050765374572008-06-04T00:49:00.001-07:002008-06-04T00:49:09.784-07:00The Day of Bugs<p>Ok, I can honestly say that today was one of the weirder days that I've had in a long while.  I don't know about others, but I can say with confidence that I've never personally identified a bug in Visual Studio in my career.  I've seen plenty of them mentioned by other folks, I've seen 'features' that I'd be inclined to call a bug (but could be interpreted either way), but I've never really found a bug myself.</p> <p>Today, I found two.  I guess it's a case of 'when it rains, it pours'.  One of them was known long before I 'found' it, but obviously not known to me.  The other, I'm pretty confident, is still unknown to 'everyone'.</p> <h1>Bug #1 - Dynamic Version vs. BAML.</h1> <p>Ok...  So we've done a fair bit of playing with WPF on my project, and we've done some custom control development (user controls) in WPF for use in our application.  WPF is very convenient for being able to prototype and design the UX/UI of something without being bogged down by all the crap you have to do to customize WinForms (our users seem to never like the 'way it is').  I can say that I feel pretty comfortable that my skills with WPF, while not the best on the block, are probably up there along with most of the folks currently doing WPF development.  I've done a ton of data binding work, and feel pretty confident that I know most of the tricks there - especially thanks to the wonderful work of <a href="http://www.beacosta.com/blog/" target="_blank">Bea Costa</a>!</p> <p>So, I was very puzzled when one of our developers started having issues with running our application's forms that use one of my user controls.  The user control was pretty simple - it was a list of names that had alternating highlights for the rows (one row was white, one was gray, etc.).  It did a few other things, but mostly that was the gist of it.  This is one of the simpler controls we have.  Anyway, the weird thing was that the 'bug' that we kept seeing only appeared when running the debug build of our application, and it only appeared when our UI was being used from the APL application.  It never appeared in release builds, and it never appeared when running debug mode in our UI test harness.</p> <p>So, naturally, I looked first at the APL runtime, thinking it was a bad install on this dev's machine.  We then took his build and his APL workspace and ran it on my machine.  To my surprise, it crashed on my machine too.  Then, we tried running one of my builds on his machine - it worked (also to my surprise!).  So then, I concluded it was a problem with his machine.</p> <p>Two days later, after he had gotten some other work done and managed to uninstall all of .NET 2.0 through 3.5, VS2005 and VS2008, and then reinstall all of them (carefully in order), he tried it again.  BOOM!  It still didn't work.  I brought him my old laptop, and I had IT set it up for him to be able to use it instead of his desktop, thinking we'd be rebuilding his desktop from scratch.  All the while, still being puzzled by the fact that the behavior ran around to different machines and environments and was so skittish.</p> <p>Later that day, he came over to my desk and told me the problem started appearing on his release builds too.  I thought, "oh great - a viral bug!".  He then said that the problem also started appearing on my builds.  At this point, I thought - "ok, there's gotta be something else going on here".</p> <p>The bad part about this bug was this - whenever you ran the application, it would look like it wanted to pop up an error dialog, in fact it would show the thread exception dialog (<a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.threadexceptiondialog.aspx" target="_blank">System.Windows.Forms.ThreadExceptionDialog</a>) briefly (actually several of them on top of each other), but then the application would disappear before you could do anything.  Apparently, looking back, the problem was on one of WPF's "special" threads and APL apparently doesn't react very nicely to the .NET AppDomain having threads other than the main UI thread throw exceptions.</p> <p>Finally recognizing that I might be able to do something about this, I went into the code and added an exception handler with a plain-old message box in it (e.ToString()).  Looking at the exception text, I saw that it said something about a XAML parse error and that my ValueConverter couldn't be loaded (I had a ValueConverter as a static resource in my XAML for getting the backcolor brush for doing the highlighting).  This error message pointed me to: <a href="http://rrelyea.spaces.live.com/blog/cns!167AD7A5AB58D5FE!497.entry" target="_blank">Rob Relyea's blog post</a> (along with several MSDN forums posts).  The only thing was that his post didn't apply completely to my issue.  But, the workarounds did.  It turned out that I was using AssemblyVersion(1.0.*) in my files (which I really like for our 'in dev' work), but it was causing problems.  It seems that the reason the bug was so 'fleeting' was that there must have been a timing issue on the fourth bit of the version number (the revision), since it's based on a timestamp.</p> <p>Apparently, my computer is too fast (most of the time), so I didn't see this bug on my builds, just on my colleagues'!  As I said, this bug has been known about for a long time, and while I'm not thrilled with the workaround, it's there and working, so I'll live with it.</p> <p>BTW, I spent nearly 4 days chasing this bug, off and on (my colleague did most of the legwork).  It wasn't much fun, being that we couldn't get an error message for the first 3 of those.</p> <h1>Bug #2 - VS2008 STL vs. NUnit, C++/CLI, and ME!</h1> <p>So, I spent the last 4 days chasing what I though <em><strong>must</strong></em> have been a bug in our code, only to figure out that I'm pretty sure it's a bug in MS's STL implementation (for Debug builds).  However, this bug is really hard to find (though it, at least, is VERY consistent - i.e. happens every time and is very reproducible).</p> <p>First, some background on our design.  Our application consists of two major pieces, a UI, and a backend calculation engine.  The UI is written in .NET 3.0/3.5 (C#), and the backend is written in C++ (native).  However, these systems need to be able to share a common file format.  To meet this need, we developed a generic file library in native C++ code that can be used to read/write files for our system.  The files are basically like MS's structured storage, but with the features and interfaces that we desired for our system (along with a design oriented towards meeting our required performance characteristics).</p> <p>All of our file structures are build upon this file library, along with several other native libraries that support it and some of the other 'shared' features.  We also have a generic 'key' library (this is a domain concept for us - you can think of it as a property bag with some special 'matching' features).</p> <p>In order to support using these libraries from both C++ and C# code, we decided we'd write the implementations in native C++ using traditional object-oriented design principles (many of which were lifted from my C#/Java experiences), and then write a thin C++/CLI (the managed C++ language) wrapper around this library using the IJW (it just works) interop supported by C++/CLI.  Then, our C# clients would call into the C++/CLI managed library (not even knowing that it's implemented in C++) just as they would call into managed C# code, but be able to use the underlying data structures and implementation of the native libraries.  I think this design is pretty elegant, and we solved quite a few interesting issues when developing it.  We've been using it now for some time and it's working quite well.</p> <p>So...  now, the bug.  Just recently we needed to add a new feature to the 'key' library.  This library is very simple and the feature was also quite simple.  I added it to the native code, added it in the managed C++/CLI library, and added the C# unit test code to exercise it.  By convention, we only run our unit tests in Release mode, unless we're debugging them, since we only want to take the time to test in one build environment and it makes most sense to test the bits that are going out the door...</p> <p>Anyway, so I tested in release mode, the tests all passed, and I checked in.  I was happy and I went home for the day.  The next day, I happened to be compiling in Debug mode and I decided to run what I was working on.  I had forgotten that my startup project in VS2008 was set to the unit tests for the 'key' library, so the unit tests ran instead of what I was intending to test.  To my surprise, the unit tests for the 'key' library 'exploded' (they didn't fail, they caused an AccessViolationException that was caught by VS2008 and popped up in the debugger!).  The AV was showing up on the destructor call for one of our unmanaged C++ objects (native library).</p> <p>To make matters worse, the bug only showed up when the finalizer was called for the class, and even weirder, only showed up when the variables were not deterministically destroyed (i.e. using IDispose and 'using').  Since my unit test code wasn't using 'using' anywhere, I saw the 'explosion'.  But, I saw it only when the finalizer was called (much later than the offending code, obviously).  I invested some time writing code to track which object was the culprit, and after figuring out which (using 'value numbering', a technique I use a lot in single-threaded debugging of applications with lots of object instances and no unique identifiers in them), I followed the code.  It was not at all obvious why there was a problem.  In fact, I looked at it for several days and couldn't figure out what the problem was.</p> <p>I then posted <a href="http://forums.msdn.microsoft.com/en-US/vclanguage/thread/f2f69a47-1a19-4116-8759-9e6b08443ab7" target="_blank">here</a>, and called my Arch. Evangelist at MS and talked with him about it, and still couldn't figure out what it is (the spoiler is the last post in the thread).  I finally had to resort to the 'commenting' technique to track down the bug.  I first commented out all unit tests and started adding them back in one by one.  Once I found which unit test failed, I commented out the entire test and started adding code back in block by block.  Once I found the offending block, I looked down into the C++ code and <strong><em>STILL </em></strong>couldn't find anything wrong with it.</p> <p>At that point, I decided I needed to think outside the box.  I looked at what was different between the various method calls that worked and didn't work, and decided that the throwing of the exception might be a problem.  First, I removed all the code in the offending method.  Now, my test failed, but it didn't explode.  So, I put the code back, and looked at the exception more thoroughly - I decided to move it to the head of the function.  That also caused my test to fail, but it didn't explode.  So then I concentrated on the code before the exception (in the original function).  I kept saying to myself - there's NOTHING wrong with this code!  (you can see the code in the <a href="http://forums.msdn.microsoft.com/en-US/vclanguage/thread/f2f69a47-1a19-4116-8759-9e6b08443ab7" target="_blank">MSDN post</a>).  Finally, I thought - "it <em><strong>has</strong></em> to be this code, so let's assume it's broken and figure out when and why".  I then restructured the code (as described in the MSDN post) and determined that it was absolutely something in the 'begin()' or 'end()' STL vector calls.  There's no way I messed that up - that's <strong><em>their</em></strong> code.  Voilà - bug #2.</p> <p>For this one, I'm going to have to figure out how to submit it to MS.  I'm sure nobody's seen this one (at least as far as I can tell from <a href="http://www.google.com/search?hl=en&q=VC2008+bug+C%2B%2B+STL+vector+empty+access+violation+debug&btnG=Google+Search" target="_blank">searching</a>).</p> <p><strong>Whew!  Looking forward (hopefully) to not having very many more of those days!</strong></p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-16141740274527269102008-05-31T00:11:00.001-07:002008-05-31T00:11:05.371-07:00Redistributing C++ runtime components<p>Ok.  I've had to look for this several times over the years.  This time, I finally decided to just post it on my blog so I can find it again later :)</p> <p><a href="http://blogs.msdn.com/vcblog/archive/2007/10/12/how-to-redistribute-the-visual-c-libraries-with-your-application.aspx" target="_blank">Here it is</a></p> <p>Hopefully it helps you sometime too...</p> Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-89875144054060886692008-05-29T20:30:00.000-07:002008-05-29T20:33:33.700-07:00StL gave Oakwood 1/2 Million Dollars???What the hell?<br /><br />Article in recent STL Business Journal:<br /><br />"St. Louis development fund invests in Oakwood Systems.Oakwood Systems Group Inc. received a $500,000 investment for expansion from the St. L Business Development Fund, which invests in St. L based companies that are unable to access sufficient senior debt to finance organic growth or acquisition.Creve Coeur MO based technology consulting firm Oakwood Systems is one of the fastest growing private companies in the St. Louis area, according to Business Journal research. The company reported $7.5 million in fiscal 2006 revenue, a 92.3 percent increase from fiscal 2004 revenue of $3.9 million. The firm has 85 employees and an office in Nashville, TN."<br /><br />If this isn't the funniest thing I've heard in a long time - it's gotta be up there. Of course this will only be funny to those of you who have heard my stories about my time at Oakwood and the goings on there.Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com1tag:blogger.com,1999:blog-8594690550975135798.post-77851354683441253902008-05-26T23:25:00.000-07:002008-05-27T00:01:16.466-07:00Patching from a single .exeWe've been doing some work lately to try to figure out a good way to make our software patches go out the door as a single .exe. We aren't using MSI for our patches because we need to be able to do some things that MSI can't do (i.e. install older patches over newer versions, verify the current system version before installing, always install files, regardless of whether they are have been modified by the user or not, etc.).<br /><br />So, I was thinking about different approaches. As I saw it, there were:<br /><ol><li>Package everything in a zip, require users to download the zip and extract it into a temp folder, and run a .exe inside the zip that does the patch. Then require the user to delete the folder on their own.</li><li>Build an .EXE that has resources in it for the files that were to be included in the patch - have the .exe extract these resources to disk in the proper locations and perform the other logic necessary.</li><li>Build a generic packaging process, package stub, and 'package runner' that can solve the problem as well as other similar problems should we ever want to package anything else as a single .EXE (besides patches).</li></ol><p>I didn't like (1) for the UX - I think it's pretty nasty UX-wise. I didn't like (2) because there are several problems - (a) the person who would be building the patches doesn't really work in VS, and I don't want them compiling the code. I also don't want to write code to 'package' the resources - it's a pain. (b) I already know one other case where we will need to package stuff into a single .exe for deployment that isn't the same as our patch process, so I'd have to build a similar project again - I hate duplicate code...</p><p>I decided to go down the path of (3). I knew from my Win32 days that the .EXE loader will be perfectly fine with an .EXE with 'junk' concatenated to the end of it. I've done this several times in the past - the PE format doesn't have any problem with 'extra' bits at the end of the file. So, I decided to go with the idea of a stub .EXE with a 'package' tacked on the end.</p><p>The next question was - what should my package support? I decided that for me a package would be two things - a list of files, and an entry assembly (that gets run by the package stub upon loading). The package would be a 'manifest' (an XML file that lists the package contents), along with a stream of bytes for the package data. For my needs, it was sufficient if all packages contained (1) a list of files, (2) an entry assembly, (3) an argument to pass to the entry assembly on running it, and (4) a list of assemblies upon which the entry assembly depended. The entry assembly would be loaded and executed by the package stub (.EXE) upon running the .EXE.</p><p>So, the file looks like:</p><br /><p><table style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; BORDER-BOTTOM: black 1px solid; BORDER-COLLAPSE: collapse; TEXT-ALIGN: center; border-spacing: 0px" bordercolor="black" border="1"><tbody><tr><td style="BACKGROUND: #7fefef">Stub .EXE</td></tr><tr><td style="BACKGROUND: #9fcfcf">Package Data Bits</td></tr><tr><td style="BACKGROUND: #bcdfaf">Package Manifest</td></tr><tr><td style="BACKGROUND: #bfbfbf">Package Trailer</td></tr></tbody></table></p><br /><p>Where the data bits are GZip compressed streams of bits corresponding to the items listed in the manifest, and the Package Trailer is a 'header' (trailer actually) that is of fixed length and identifies this file as a valid package (i.e. has a identifying 'magic number') and contains offsets of the various items within the file (i.e. the location of the first byte of data bits, and the length of the manifest).</p><p>Since my support code for this functionality is in a class library (I called it 'Packaging'), the class library is embedded in the stub .exe as a resource. The stub performs the following steps when it is run:</p><ol><li>load the resource "Packaging" and call Assembly.Load on the byte array.</li><li>set up a 'resolver' to resolve this assembly for the other assemblies that might be loaded by this package.</li><li>unpackage the dependencies of the entry assembly and load them as well</li><li>unpackage the entry assembly</li><li>look for a 'Package' class in the global namespace of the entry assembly, and cast it to an IPackage (defined in Packaging assembly)</li><li>call 'Execute' method on the IPackage from (5), passing an IPackageHost interface that provides functionality for reading the contents of the package and interacting with the stub.</li></ol><p>(2) is an event handler attached to the AppDomain.CurrentDomain.AssemblyResolve event.</p><p>The IPackageHost is an interface that allows the IPackage.Execute implementation to gain access to the entries (and bits) in the manifest. This host interface is provided by the stub, but can also be implemented by other code in order to do testing, extract packages, run them separately, etc. In fact, my Packaging library provides the implementation of IPackageHost via a PackageHost class that is used by the stub .EXE to provide the necessary support to the entry assembly's IPackage implementation.</p><p>If you want more details on how this all works, give me a call, or shoot me an email - I'll be happy to provide more details.</p>Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-41727265586123850192008-05-24T23:34:00.001-07:002008-05-24T23:36:41.490-07:00Another great ALT.NET event!Yet another great ALT.NET event today. Thanks Glenn for the conversation in the car on the way there (and congrats / good luck on your new job on MEF), thanks Justin for the space, and thanks everyone else for the great conversations.<br /><br />Hopefully people are interested in coming to my place in July or early Aug for one.<br /><br />I'll try to be more diligent about blogging some of my recent dev work. I think it's pretty interesting and I haven't found similar content elsewhere.Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0tag:blogger.com,1999:blog-8594690550975135798.post-9481175013557532872008-04-21T09:51:00.000-07:002008-05-29T23:52:07.939-07:00Coolest weekend ever.Ok... the weather sucked - it was snowing here - but otherwise this weekend was awesome. I got to meet a lot of great people, and some real top minds in the field. Who did I meet? Among others - Brad Abrams (.NETfx team, coauthor of framework guidelines book), Jeremy Miller (the StructureMap guy), Glenn Block (P&P guy), Scott Hanselman (goes without introduction), Martin Fowler (the 'Refactoring' book guy), Charlie Poole (the NUnit guy) ... the list goes on (no offense please if I met you and didn't include you in this list).<br /><br />I had a wonderful lunch conversation with Brad for about 20-30 minutes over lunch. It's amazing to have someone like Brad come sit down next to you, introduce himself, and then share a one-on-one conversation with you. It'd be like Fisher Black coming and sitting down next to an actuary and introducing himself and asking about your work. It's very hard to not feel intimidated in such a situation, but not only was Brad not intimidating, but he's a really genuinely nice guy to talk to. Hopefully I'll have the luck of meeting him again and having more conversations. hehehe... maybe he'll ask me if I want a job someday :) I can dream, right?<br /><br />All in all, this is the best work-related event I've ever been to, and it's probably the best weekend of my working life thus far. I definitely consider the 30 minutes I spend with Brad to be the best 30 minutes of my career to this point.<br /><br />If you ever hear of an 'open spaces' event, I highly recommend you jump on the opportunity to attend. Also, if you ever want to go to ALT.NET events, hopefully I'll see you there.Kelly Leahyhttp://www.blogger.com/profile/01587387873239212889noreply@blogger.com0