tag:blogger.com,1999:blog-244027812024-03-07T00:56:20.352-08:00Jason Hales - C# TipsSharing my day-to-day experiences as a C# developerJason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-24402781.post-34257074832097701142015-03-01T12:06:00.003-08:002015-03-04T05:05:28.450-08:00Async Await By Example - Part 1When I first started using C#’s async/await keywords I found it helpful to create a set of notes as I progressed. On this post you’ll find a collection of tips and how-tos. Hopefully others will find it useful.<br />
<br />
<h3>
Quick Tips</h3>
<div>
<div>
* Each managed thread in .NET reserves around 1MB of virtual memory</div>
<div>
* Only a method marked async can contain the await keywords</div>
</div>
<div>
<br /></div>
<h3>
Using NUnit to Run Test Methods</h3>
<div>
<div>
I’m using NUnit as a convenience to step through my test code. As of v2.6.2, NUnit supports unit tests with the async keyword. </div>
<div>
<br /></div>
<div>
<a href="http://nunit.org/index.php/index.php?p=releaseNotes&r=2.6.2">http://nunit.org/index.php/index.php?p=releaseNotes&r=2.6.2</a></div>
<div>
<br /></div>
<blockquote class="tr_bq">
<i> Beginning with NUnit 2.6.2, test methods targetting .Net 4.5 may be marked as async and NUnit will wait for the method to complete before recording the result and moving on to the next test.Async test methods may return void or Task if no value is returned, or Task<T> if a value of type T is returned.</i></blockquote>
<div>
<br />
<a name='more'></a><br /></div>
<div>
To support the test code here, I’ve added a general logger class that extracts the calling method and thread id:</div>
</div>
<div>
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">Logger</span></div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">{
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Log(<span style="color: blue;">string</span> message, [<span style="color: #2b91af;">CallerMemberName</span>]<span style="color: blue;">string</span> caller = <span style="color: blue;">null</span>)
{
LogInternal(message, caller);
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> LogInternal(<span style="color: blue;">string</span> message, <span style="color: blue;">string</span> caller)
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0:HH:mm:ss.ff}</span><span style="color: #a31515;"> (</span><span style="color: mediumseagreen;">{1:00}</span><span style="color: #a31515;">) [</span><span style="color: mediumseagreen;">{2}</span><span style="color: #a31515;">]: </span><span style="color: mediumseagreen;">{3}</span><span style="color: #a31515;">"</span>,
<span style="color: #2b91af;">DateTime</span>.Now,
<span style="color: #2b91af;">Thread</span>.CurrentThread.ManagedThreadId,
caller, message);
}
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
The following test has <b><i>Timeout </i></b>attribute assigned so it will fail as the task is not returned with 1000 milliseconds):<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">[<span style="color: #2b91af;">Test</span>]
[<span style="color: #2b91af;">Timeout</span>(1000)]
<span style="color: blue;">public</span> <span style="color: blue;">async</span> <span style="color: #2b91af;">Task</span> AsyncLambdaTimeout()
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Start"</span>);
<span style="color: #2b91af;">Func</span><<span style="color: #2b91af;">Task</span>> slowTask = <span style="color: blue;">async</span> () =>
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Inside.."</span>);
<span style="color: blue;">await</span> <span style="color: #2b91af;">Task</span>.Run(() =>
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Sleeping1..."</span>);
<span style="color: #2b91af;">Task</span>.Delay(1500).Wait();
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"...Awake1"</span>);
});
};
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Before await"</span>);
<span style="color: blue;">await</span> slowTask();
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"End"</span>);
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
Rest results: <br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:03:31.21 (09) [AsyncLambdaTimeout]: Start </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:03:31.23 (09) [AsyncLambdaTimeout]: Before await </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:03:31.24 (09) [AsyncLambdaTimeout]: Inside.. </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:03:31.25 (04) [AsyncLambdaTimeout]: Sleeping1... </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Test exceeded Timeout value of 1000ms </span></div>
<div>
<br /></div>
<div>
Notice how Logger.Log("...Awake1") never gets called as we waited 1500 milliseconds prior.</div>
<br />
<h3>
Return Statement </h3>
<div>
The return value from async method can be void, Task or Task<T>. </div>
<div>
void is rarely used unless it’s truly a fire and forget operation (it's best to return async Task as easier for caller to use await to wait and makes exception handling easier) </div>
<div>
For void and Task the return statement is optional or must be just “return;” </div>
<div>
Task<T> must return a type T (the compiler will wrap this in a Task<T> for you)</div>
<div>
<br /></div>
<div>
<h3>
Where async Cannot be Used</h3>
<div>
<br /></div>
<h4>
1. Catch (although that’s now changed in C#6)</h4>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">async</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> InvalidInCatch()
{
<span style="color: blue;">var</span> result = <span style="color: blue;">string</span>.Empty;
<span style="color: blue;">try</span>
{
result = <span style="color: blue;">await</span> SlowRunningMethodWithError();
}
<span style="color: blue;">catch</span> (<span style="color: #2b91af;">Exception</span>)
{
<span style="background-color: yellow;"> <span style="color: green;">// Give compiler error: Cannot await in the body of a catch clause</span>
result = <span style="color: blue;">await</span> SlowRunningMethod();</span>
}
<span style="color: blue;">return</span> result;
}</pre>
<h4>
2. Lock</h4>
<div>
There’s no point in locking on an object as the calling thread will release the synclock but get called back on a different thread. </div>
</div>
<div>
<br /></div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: blue;">object</span> _sync = <span style="color: blue;">new</span> <span style="color: blue;">object</span>();
[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">async</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> InvalidInLock()
{
<span style="color: blue;">var</span> result = <span style="color: blue;">string</span>.Empty;
<span style="color: blue;">var</span> sync = <span style="color: blue;">new</span> <span style="color: blue;">object</span>();
<span style="color: blue;">lock</span> (_sync)
{
<span style="background-color: yellow;"><span style="color: green;">// Gives compiler error:
// The 'await' operator cannot be used in the body of a lock statement</span>
result = <span style="color: blue;">await</span> SlowRunningMethod();</span>
}
<span style="color: blue;">return</span> result;
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
<h4>
3. Most parts of linq</h4>
<h4>
<br />4. Unsafe code</h4>
</div>
<div>
<br /></div>
<div>
<h3>
</h3>
<h3>
Nothing to Return From an Async Call?</h3>
<div>
<br /></div>
<div>
If you’ve got an async call that does return anything, ie some sort of fire and forget operation, it’s best to use async Task</div>
</div>
<div>
<br /></div>
<div>
<h3>
Exceptions</h3>
<div>
Running this await call, the exception will be caught:</div>
</div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: white;">[</span><span style="background-color: white; color: #2b91af;">Test</span><span style="background-color: white;">]
</span><span style="background-color: white; color: blue;">public</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">async</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">Task</span><span style="background-color: white;"><</span><span style="background-color: white; color: blue;">string</span><span style="background-color: white;">> AwaitCatchException()
{
</span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> result = </span><span style="background-color: white; color: #a31515;">""</span><span style="background-color: white;">;
</span><span style="background-color: white; color: blue;">try</span><span style="background-color: white;">
{
result = </span><span style="background-color: white; color: blue;">await</span><span style="background-color: white;"> SlowRunningMethodWithError();
}
</span><span style="background-color: white; color: blue;">catch</span><span style="background-color: white;"> (</span><span style="background-color: white; color: #2b91af;">Exception</span><span style="background-color: white;"> ex)
{
</span><span style="background-color: yellow;"> <span style="color: green;">// Will catch the exception</span>
<span style="color: #2b91af;">Logger</span>.Log(ex.ToString());</span><span style="background-color: white;">
}
</span><span style="background-color: white; color: blue;">return</span><span style="background-color: white;"> result;
}</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> SlowRunningMethodWithError()</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"SLOWIE: START"</span>);
<span style="color: blue;">var</span> delay = <span style="color: #2b91af;">Task</span>.Run(() =>
{
<span style="color: #2b91af;">Task</span>.Delay(2000).Wait();
<span style="color: blue;">throw</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">ApplicationException</span>(<span style="color: #a31515;">"oops"</span>);
<span style="color: blue;">return</span> <span style="color: #2b91af;">DateTime</span>.Now.ToString(<span style="color: #a31515;">"HH:mm:ss.ff"</span>);
});
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"SLOWIE: END"</span>);
<span style="color: blue;">return</span> delay;
}</pre>
</pre>
</pre>
</div>
Results:<br />
The MoveNext method name – line 17 in my case does refer to this line:<br />
result = await SlowRunningMethodWithError();<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:42:26.50 (05) [SlowRunningMethodWithError]: SLOWIE: START</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:42:26.52 (05) [SlowRunningMethodWithError]: SLOWIE: END</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:42:28.55 (04) [CatchExceptions]: System.ApplicationException: oops</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at AsyncAwaitDemo.AABasics.ExceptionTests.<SlowRunningMethodWithError>b__4() in ExceptionTests.cs:line 34</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Threading.Tasks.Task`1.InnerInvoke()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Threading.Tasks.Task.Execute()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">--- End of stack trace from previous location where exception was thrown ---</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at ExceptionTests.<CatchExceptions>d__0.MoveNext() in ExceptionTests.cs:line 17</span><br />
<div>
<br /></div>
<div>
Conversely, calling SlowRunningMethodWithError as a task, the exception will only be caught when you await the task:</div>
<div>
<span style="background-color: white;">[</span><span style="background-color: white; color: #2b91af;">Test</span><span style="background-color: white;">]</span></div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: white; color: blue;">public</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">async</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">Task</span><span style="background-color: white;"><</span><span style="background-color: white; color: blue;">string</span><span style="background-color: white;">> NeedsAwaitToGetException()
{
</span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> result = </span><span style="background-color: white; color: #a31515;">""</span><span style="background-color: white;">;
</span><span style="background-color: white; color: green;">// Note exception will fire when we call await - not in the next line</span><span style="background-color: white;">
</span><span style="background-color: white; color: green;">// this behaviour is a change to .NET!!!!</span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> task = SlowRunningMethodWithError();
</span><span style="background-color: white; color: blue;">try</span><span style="background-color: white;">
{
</span><span style="background-color: yellow;"><span style="color: green;">// exception will be trigged here</span>
result = <span style="color: blue;">await</span> task;</span><span style="background-color: white;">
}
</span><span style="background-color: white; color: blue;">catch</span><span style="background-color: white;"> (</span><span style="background-color: white; color: #2b91af;">ApplicationException</span><span style="background-color: white;"> ex)
{
</span><span style="background-color: white; color: green;">// Will catch the exception</span><span style="background-color: white;">
</span><span style="background-color: white; color: #2b91af;">Logger</span><span style="background-color: white;">.Log(ex.ToString());
}
</span><span style="background-color: white; color: blue;">return</span><span style="background-color: white;"> result;
}</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">Output:</pre>
</div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:58:21.49 (05) [SlowRunningMethodWithError]: SLOWIE: START</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:58:21.52 (05) [SlowRunningMethodWithError]: SLOWIE: END</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">18:58:23.55 (04) [NeedsAwaitToGetException]: System.ApplicationException: oops</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at ExceptionTests.cs:line 59</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Threading.Tasks.Task`1.InnerInvoke()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Threading.Tasks.Task.Execute()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">--- End of stack trace from previous location where exception was thrown ---</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">at ExceptionTests.cs:line 40</span><br />
<br />
<h3>
Recommendations on When to Await</h3>
<div>
<br /></div>
The synchronous parts in an async method, ie up to making an async all should be minimal, e.g. checking arguments, looking in a cache to avoid the async call.<br />
<br />
<h3>
Is it Running Async?</h3>
<br />
Just because you’re using an async method doesn’t mean it’s going to get called in an async manner. Methods start synchronously until an async method calls await<br />
Given this slow running method that returns a Task<string><br />
<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> SlowRunningMethod()
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"SLOWIE: START"</span>);
<span style="color: blue;">var</span> delay = <span style="color: #2b91af;">Task</span>.Run(() =>
{
<span style="color: #2b91af;">Task</span>.Delay(2000).Wait();
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"After delay"</span>);
<span style="color: blue;">return</span> <span style="color: #2b91af;">DateTime</span>.Now.ToString(<span style="color: #a31515;">"HH:mm:ss.ff"</span>);
});
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"SLOWIE: END"</span>);
<span style="color: blue;">return</span> delay;
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
Running these two tests:<br />
<br />
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">async</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> AwaitDirect()
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Start"</span>);
<span style="color: green;">// Following line is ran async - will have diffrent thread id</span>
<span style="color: blue;">var</span> retValue = <span style="color: blue;">await</span> SlowRunningMethod();
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"End. RetValue=</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">"</span>, retValue));
<span style="color: blue;">return</span> retValue;
}</pre>
</pre>
<pre style="background: white;"></pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">async</span> <span style="color: #2b91af;">Task</span><<span style="color: blue;">string</span>> DefineAsTaskAndAwait()
{
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"Start"</span>);
<span style="color: green;">// The method will start on the current thread - so will block it</span>
<span style="color: green;">// it will return a Task<string> in the current thread</span>
<span style="color: blue;">var</span> slowTask = SlowRunningMethod();
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: #a31515;">"In the middle"</span>);
<span style="color: blue;">var</span> retValue = <span style="color: blue;">await</span> slowTask;
<span style="color: #2b91af;">Logger</span>.Log(<span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"End. RetValue=</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">"</span>, retValue));
<span style="color: blue;">return</span> retValue;
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: transparent;">Give this result:</span></pre>
</pre>
<br />
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;"><tbody>
<tr><td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 225.4pt;" valign="top" width="301"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>AwaitDirect<o:p></o:p></b></div>
</td><td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 225.4pt;" valign="top" width="301"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>DefineAsTaskAndAwait<o:p></o:p></b></div>
</td></tr>
<tr><td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 225.4pt;" valign="top" width="301"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(05) [AwaitDirect]: Start<br />(05) [SlowRunningMethod]: SLOWIE: START<br />(05) [SlowRunningMethod]: SLOWIE: END<br /><br /><o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(04) [SlowRunningMethod]: After delay<br />(04) [AwaitDirect]: End. RetValue=18:06:28.26</span><o:p></o:p></div>
</td><td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 225.4pt;" valign="top" width="301"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(05) [DefineAsTaskAndAwait]: Start<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(05) [SlowRunningMethod]: SLOWIE: START<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(05) [SlowRunningMethod]: SLOWIE: END<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
(05) [DefineAsTaskAndAwait]: In the middle</div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(04) [SlowRunningMethod]: After delay<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Courier New"; font-size: 8pt;">(04) [DefineAsTaskAndAwait]: End. RetValue=18:08:43.89<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
</td></tr>
</tbody></table>
<br />
<div class="MsoNormal">
</div>
<br />
<div class="MsoNormal" style="orphans: auto; text-align: start; text-indent: 0px; widows: auto;">
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; margin: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">
<br /></div>
<h3 style="margin: 0px;">
State Machine</h3>
<div>
Prior to calling to awaitable code, the compiler will generate a state machine containing a copy of all local variables:</div>
<br />
<ul>
<li>Parameters to the method</li>
<li>Variables in scope</li>
<li>Other variables, eg loop counters</li>
<li>this is the method if non-static</li>
</ul>
<br />
<br />
<i>More to follow</i></div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com2tag:blogger.com,1999:blog-24402781.post-14236413306162739782015-02-26T05:46:00.000-08:002015-03-04T05:04:59.016-08:00Using ImportMany For Lazy Initialisation With Reactive Extensions (RX) and WPF/Prism<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="MsoNormal">
Working on a WPF application, I had a requirement to allow a
user to monitor the status of a number of connected services – the status of some
services could fluctuate many times per second.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
There were a couple of issues that needed to be considered:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<ol>
<li>How would I get
hold of these monitored ‘services’? Could
I just create new instances and pass them into my monitoring class? Unfortunately not, as I didn’t know until
runtime what these services were.<br /> </li>
<li>How would I throttle the status updates to make them more
presentable to the user without resulting in some sort of flashing Christmas tree
effect?<br /> </li>
<li>How would I unit test the monitoring behaviour?<br /> </li>
<li>How would I display the status of specific items in an
easy to understand manner? Did I want to
wait for a disconnect status and show that in, say, red? </li>
</ol>
<o:p></o:p><br />
<a name='more'></a><br /><br />
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
I came up with a solution that had the following features:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<ul>
<li>Use the Managed Extensibility Framework (MEF) to discover,
at runtime, a collection of lazily loaded classes which implement and export a specific
interface (my <i>IConnectable</i> in this case).<br /> </li>
<li>Use Reactive Extensions (RX) to throttle the frequency of
updates in a more user friendly frequency - using the Sample() extension
method.<br /> </li>
<li>Use WPF Bindings and a Converter to format the appearance
of individual items in a DataGrid.<br /> </li>
<li>Extend <b>INotifyPropertyChanged</b>
to include compiled properties for improved speed and change notification.<br /> </li>
<li>Use NUnit and Reactive Testing’s <b>TestScheduler</b> to be able to move time forward to confirm that frequently
updated status updates are indeed throttled back.<br /> </li>
<li>Use PRISM’s MefBootstrapper to provide a basic Shell
container (NB: this is by no means an exercise on how to design region specific
Shell containers…I’ll save that for another day)</li>
</ul>
<o:p></o:p><br />
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Before I go into details, the running screens look like this
(clearly this is not an exercise in engaging screen design – I’m using the
standard <b>DataGrid</b>):<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Splash screen at
start-up:<o:p></o:p></b></div>
<div class="MsoNormal">
<b><br /></b></div>
<div class="MsoNormal">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiGkcADMG76j-02CMrT8GSGXCpNGuDUtdmkWQNwOQ02S7Y7o787SRVVIawO_5ColhrF3C7Ghx-n80m7xaNMz9qBI0PSh_IsU-LzKaXoKphaj8f1bE7WjvZ3HcNj_Pa6HmYXakg/s1600/Splash.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiGkcADMG76j-02CMrT8GSGXCpNGuDUtdmkWQNwOQ02S7Y7o787SRVVIawO_5ColhrF3C7Ghx-n80m7xaNMz9qBI0PSh_IsU-LzKaXoKphaj8f1bE7WjvZ3HcNj_Pa6HmYXakg/s1600/Splash.jpg" height="320" width="281" /></a></div>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Status screen in
action:<o:p></o:p></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvFjihyphenhyphen3kPnccsCil9j5-suO90A8Dp-fvtTTRvFQsxgjWz32IsTYOLtazEs7oev0LrHPXn2dxFvkPl-uaYEWLvPAC7IxRExpRDcbqr7RKV3N4q9QJ5SxJ2mQZW8MDQXdUejAs/s1600/MainScreen.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvFjihyphenhyphen3kPnccsCil9j5-suO90A8Dp-fvtTTRvFQsxgjWz32IsTYOLtazEs7oev0LrHPXn2dxFvkPl-uaYEWLvPAC7IxRExpRDcbqr7RKV3N4q9QJ5SxJ2mQZW8MDQXdUejAs/s1600/MainScreen.jpg" height="400" width="400" /></a></div>
<br />
<div class="MsoNormal">
I’m going to use separate assemblies for the key areas of
the solution:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNuh4WbbccYA7jfnMY_LM8kzVNGfSgwys_lMxz_d8NwMEFfV7aO8ucHnlgmYVYZzilP9-LX4TpEqTCJcWQ8S9kDlLrR5-NZXiPXvYYB7Grm3KlH0FhLBtLzeCyLmVaw1NI0n-7/s1600/solution.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNuh4WbbccYA7jfnMY_LM8kzVNGfSgwys_lMxz_d8NwMEFfV7aO8ucHnlgmYVYZzilP9-LX4TpEqTCJcWQ8S9kDlLrR5-NZXiPXvYYB7Grm3KlH0FhLBtLzeCyLmVaw1NI0n-7/s1600/solution.jpg" height="640" width="243" /></a><br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>PricingServices</b>:
collection of dummy “services” that implement my <i>IConnectable</i> interface. These
all perform the same task of notifying when a connection status changes, albeit
at differing times. This assembly is
copied to an <b><i>Extensions</i></b><i> </i>folder at
compile time – the UI <i>AppBootStrapper</i>
looks in that folder at runtime (see later section).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>ServiceDashboard.Core:
</b>core interfaces and abstract classes shared amongst projects.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>ServiceDashboard.Presentation</b>:
view model that react to changes in connection status (you could argue that
these ought to be defined as Models).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>ServiceDashboard.UI</b>:
WPF application that displays the results from the <i>ConnectablesStatusViewModel.<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>ServiceDashboard.UnitTest</b>:
unit tests for the solution.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h2>
<b>Task 1 – Define a
Connectable Interface</b></h2>
<div class="MsoNormal">
<i>Note: you’ll need to
run “install-package Rx-PlatformServices” into Package Manager to include the
RX PlatformService and related assemblies from NuGet.<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In the Core project (ServiceDashboard.Core) I need to add the
interface which each connectable service must implement and Export via MEF in
order to be discovered (<i>IConnectable</i>):<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">namespace</span><span style="font-family: Consolas; font-size: 10pt;"> ServiceDashboard.Core<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;"> public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">enum</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Unknown,<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Connected,<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Disconnected,<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Error<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">interface</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IConnectable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IObservable</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">> Status { </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">get</span><span style="font-family: Consolas; font-size: 10pt;">; }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> Description { </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">get</span><span style="font-family: Consolas; font-size: 10pt;">; }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
When the app starts, MEF will <i>discover</i> all classes decorated with the Export attribute and the IConnectable
type (more on that later).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The Status property is an observable so that we can listen
changes in the connection status – it’s up to each concrete implementation to
determine how and when the status actually changes (as you’d expect in a real
system).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ve also added, <i>NotifyPropertyChanged.cs</i>,
a standard implementation of <b>INotifyPropertyChanged</b>
interface (using CallerMemberName to determine the calling property)<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Collections.Generic;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Runtime.CompilerServices;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">abstract</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">NotifyPropertyChanged</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">INotifyPropertyChanged</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">event</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">PropertyChangedEventHandler</span><span style="font-family: Consolas;"> PropertyChanged;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">virtual</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> OnPropertyChanged([</span><span style="color: #2b91af; font-family: Consolas;">CallerMemberName</span><span style="font-family: Consolas;">]</span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;"> propertyName = </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> handler = PropertyChanged;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">if</span><span style="font-family: Consolas;"> (handler != </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> handler(</span><span style="color: blue; font-family: Consolas;">this</span><span style="font-family: Consolas;">, </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">PropertyChangedEventArgs</span><span style="font-family: Consolas;">(propertyName));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">bool</span><span style="font-family: Consolas;"> SetAndNotifyIfChanged<T>(</span><span style="color: blue; font-family: Consolas;">ref</span><span style="font-family: Consolas;"> T backingField, T newValue, <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> [</span><span style="color: #2b91af; font-family: Consolas;">CallerMemberName</span><span style="font-family: Consolas;">] </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;"> propName = </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">if</span><span style="font-family: Consolas;"> (</span><span style="color: #2b91af; font-family: Consolas;">EqualityComparer</span><span style="font-family: Consolas;"><T>.Default.Equals(backingField, newValue))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">false</span><span style="font-family: Consolas;">;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> backingField = newValue;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> OnPropertyChanged(propName);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">true</span><span style="font-family: Consolas;">;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I also need a common extension method that extends INotifyPropertyChanged,
which I’ll use in my unit tests to return observable changes in “known”
properties. This uses a compiled
Expression that points to a property getter, rather a string representation of
a class’s property (see <i>FromPropertyChangedPattern(item
=> item.Status).Subscribe()</i> in the unit tests)<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
NotifyPropertyChangedExtensions.cs:<o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Linq.Expressions;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reactive;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reactive.Linq;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reflection;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">NotifyPropertyChangedExtensions</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IObservable</span><span style="font-family: Consolas;"><TValue> FromPropertyChangedPattern<TSource, TValue>(<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">this</span><span style="font-family: Consolas;"> TSource source,<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Expression</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Func</span><span style="font-family: Consolas;"><TSource, TValue>> property)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">where</span><span style="font-family: Consolas;"> TSource : </span><span style="color: #2b91af; font-family: Consolas;">INotifyPropertyChanged</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">from</span><span style="font-family: Consolas;"> _ </span><span style="color: blue; font-family: Consolas;">in</span><span style="font-family: Consolas;"> FromPropertyChangedPattern(source, GetPropertyInfo(property))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">let</span><span style="font-family: Consolas;"> getter = property.Compile()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">select</span><span style="font-family: Consolas;"> getter(source);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IObservable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">EventPattern</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">PropertyChangedEventArgs</span><span style="font-family: Consolas;">>> FromPropertyChangedPattern(</span><span style="color: #2b91af; font-family: Consolas;">INotifyPropertyChanged</span><span style="font-family: Consolas;"> source, </span><span style="color: #2b91af; font-family: Consolas;">PropertyInfo</span><span style="font-family: Consolas;"> propertyInfo)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> changes = </span><span style="color: #2b91af; font-family: Consolas;">Observable</span><span style="font-family: Consolas;">.FromEventPattern<</span><span style="color: #2b91af; font-family: Consolas;">PropertyChangedEventHandler</span><span style="font-family: Consolas;">, </span><span style="color: #2b91af; font-family: Consolas;">PropertyChangedEventArgs</span><span style="font-family: Consolas;">><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> (eh => source.PropertyChanged += eh,<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> eh => source.PropertyChanged -= eh);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> changes<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Where(e => </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;">.Equals(e.EventArgs.PropertyName, propertyInfo.Name, </span><span style="color: #2b91af; font-family: Consolas;">StringComparison</span><span style="font-family: Consolas;">.Ordinal));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">PropertyInfo</span><span style="font-family: Consolas;"> GetPropertyInfo<TSource, TProperty>(<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Expression</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Func</span><span style="font-family: Consolas;"><TSource, TProperty>> propertyLambda)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> type = </span><span style="color: blue; font-family: Consolas;">typeof</span><span style="font-family: Consolas;">(TSource);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> member = propertyLambda.Body </span><span style="color: blue; font-family: Consolas;">as</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">MemberExpression</span><span style="font-family: Consolas;">;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">if</span><span style="font-family: Consolas;"> (member == </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">throw</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ArgumentException</span><span style="font-family: Consolas;">(<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;">.Format(</span><span style="color: #a31515; font-family: Consolas;">"[</span><span style="color: mediumseagreen; font-family: Consolas;">{0}</span><span style="color: #a31515; font-family: Consolas;">] is a method, property expected"</span><span style="font-family: Consolas;">, <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> propertyLambda));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> propInfo = member.Member </span><span style="color: blue; font-family: Consolas;">as</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">PropertyInfo</span><span style="font-family: Consolas;">;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">if</span><span style="font-family: Consolas;"> (propInfo == </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">throw</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ArgumentException</span><span style="font-family: Consolas;">(<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;">.Format(</span><span style="color: #a31515; font-family: Consolas;">"[</span><span style="color: mediumseagreen; font-family: Consolas;">{0}</span><span style="color: #a31515; font-family: Consolas;">] is field, property expected"</span><span style="font-family: Consolas;">, <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> propertyLambda));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">if</span><span style="font-family: Consolas;"> (type != propInfo.ReflectedType && <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> propInfo.ReflectedType != </span><span style="color: blue; font-family: Consolas;">null</span><span style="font-family: Consolas;"> &&<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> !type.IsSubclassOf(propInfo.ReflectedType))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">throw</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ArgumentException</span><span style="font-family: Consolas;">(<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;">.Format(</span><span style="color: #a31515; font-family: Consolas;">"[</span><span style="color: mediumseagreen; font-family: Consolas;">{0}</span><span style="color: #a31515; font-family: Consolas;">] unknown property"</span><span style="font-family: Consolas;">, <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> propertyLambda));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> propInfo;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Finally I’ll add IConnectablesStatusViewModel and IConnectableStatusItem. These are interfaces to that represent the
view model - I don’t want any classes referencing concrete classes as we’re
using <a href="http://martinfowler.com/eaaCatalog/separatedInterface.html" target="_blank">Separated Interfaces</a> throughout. <o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Collections.Generic;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">interface</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IConnectableStatusItem</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">INotifyPropertyChanged</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">string</span><span style="font-family: Consolas;"> Description { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;"> Status { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">TimeSpan</span><span style="font-family: Consolas;"> TimeStamp { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">interface</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IConnectablesStatusViewModel</span><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectableStatusItem</span><span style="font-family: Consolas;">> Connections { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<h2>
<b>Task 2 – Using MEF to
Discover Connectable Services (Part 1)</b></h2>
<div class="MsoNormal">
We’ve got the core interfaces defined, now it’s time to use looks
at MEF’s ImportMany attribute.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
By decorating a class’s constructor with the <i>ImportingConstructor</i> attribute we’re
asking MEF to use this particular constructor when composing a part. In addition, we can decorate any argument in
the constructor like this: <i>[ImportMany]
IEnumerable<Lazy<T>> arg<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
This tells MEF to go off, at runtime, and find all objects
that are marked with the [Export(typeof(T))] attribute. Note the use of the <i>Lazy</i> class – this provides support for Lazy initialisation –
without it you’ll get a composition exception.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
Exactly how MEF determines which assemblies to go looking is
very configurable – more on that later.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Turning to ConnectablesStatusViewModel, this is the main
view model responsible for listening to the list of <i>IConnectable</i> objects and converting those status changes into an
observable list of items that we can bind to in WPF.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I want MEF to create an instance of my <i>ConnectablesStatusViewModel</i> whenever any requests is made for an IConnectablesStatusViewModel,
so I’ll decorate the class with <i>Export(typeof(IConnectablesStatusViewModel)). </i>I’ll
need to remember to use the corresponding Import attribute later on my WPF view:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Collections.Generic;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel.Composition;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Linq;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reactive.Concurrency;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reactive.Disposables;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Reactive.Linq;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> ServiceDashboard.Core;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">[</span><span style="color: #2b91af; font-family: Consolas;">Export</span><span style="font-family: Consolas;">(</span><span style="color: blue; font-family: Consolas;">typeof</span><span style="font-family: Consolas;">(</span><span style="color: #2b91af; font-family: Consolas;">IConnectablesStatusViewModel</span><span style="font-family: Consolas;">))]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectablesStatusViewModel</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">IConnectablesStatusViewModel</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> [</span><span style="color: #2b91af; font-family: Consolas;">ImportingConstructor</span><span style="font-family: Consolas;">]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> ConnectablesStatusViewModel([</span><span style="color: #2b91af; font-family: Consolas;">ImportMany</span><span style="font-family: Consolas;">] </span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {}<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectableStatusItem</span><span style="font-family: Consolas;">> Connections { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">set</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><o:p> </o:p></pre>
<pre style="background: white;"><o:p> </o:p></pre>
<div class="MsoNormal">
When the constructor is called via MEF, I need to walk
through each of the <i>IConnectable</i>
instances passed in and listen for changes in each item’s Status property. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To do this, I’ll create a subscription to the Status
observable, adding the resulting IDisposable object to a CompositeDisposable
instance (making it easy to dispose all subscriptions in one call). <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">readonly</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">CompositeDisposable</span><span style="font-family: Consolas;"> _disposables = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">CompositeDisposable</span><span style="font-family: Consolas;">();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> ConnectablesStatusViewModel(</span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connections = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">ConnectableStatusItem</span><span style="font-family: Consolas;">>();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">foreach</span><span style="font-family: Consolas;"> (</span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectable </span><span style="color: blue; font-family: Consolas;">in</span><span style="font-family: Consolas;"> connectables<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(lazy => lazy.Value)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .OrderBy(c => c.Description))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> statusItem = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectableStatusItem</span><span style="font-family: Consolas;">(connectable.Description);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> connections.Add(statusItem);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> subscription = connectable.Status<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Subscribe(newStatus =><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.Status = newStatus;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.TimeStamp = </span><span style="color: #2b91af; font-family: Consolas;">DateTime</span><span style="font-family: Consolas;">.Now.TimeOfDay;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> });<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> _disposables.Add(subscription);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Connections = connections;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><o:p> </o:p></pre>
<div class="MsoNormal">
I loop through each of the connectable items (sorted for
convenience by description), creating a <i>ConnectableStatusItem</i>
item which takes the connectable’s Description as a constructor and then simply
listen for a change in the connectable’s Status.<o:p></o:p></div>
<div class="MsoNormal">
<b><i>Note, I’m using List<ConnectableStatusItem> rather than an
ObservableCollection< ConnectableStatusItem> as the list of connectables
will not change as the app runs (I’m getting them all right here, right now).<o:p></o:p></i></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
That’s the guts of the <i>ConnectablesStatusViewModel</i>
created. I’ll extend the class to
inherit from IDisposable to ensure my subscriptions are disposed of when
Dispose is called on my View Model:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> Dispose()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _disposables.Dispose();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<h2>
<b>Task 3 – Using RX to Stem
the Flow of Updates</b></h2>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
So I’ve set up a subscription for each of the IConnectable
instances discovered. I could now set my
ViewModel as the DataContext of a WPF window, but if any of the IConnectable
instances repeatedly update their status, I’ll end up with that annoying
flashing Christmas tree look, which I’m trying to prevent.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In the sample PriceServices project there are a number of dummy
services configured to do just that; they update their status every 100
milliseconds or so:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> FastTickingServiceBase(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> millisecondTicks) <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _tickSource = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Observable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Interval(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">.FromMilliseconds(millisecondTicks))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Subscribe(_ => GenerateConnectionStatus());<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Ideally I want to buffer these updates to more usable number. I’ve decided to use a window of one second
for updates, so if multiple status updates arrive within that interval then
I’ll just take the latest update, if any, and present that.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
This is easily achieved using the Sample() extension method. So I’ll change the subscription code to this:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> SamplePeriodInSeconds = 1;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> sampleInterval = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">.FromSeconds(SamplePeriodInSeconds);<o:p></o:p></span></div>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> subscription = connectable.Status<o:p></o:p></span></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> .Sample(</span></b><span style="font-family: Consolas;">sampleInterval<b>)<o:p></o:p></b></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Subscribe(newStatus =><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.Status = newStatus;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.TimeStamp = </span><span style="color: #2b91af; font-family: Consolas;">DateTime</span><span style="font-family: Consolas;">.Now.TimeOfDay;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> });<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
That’s it. Although,
there is one big step that I need to do if I want this to be unit testable
…more on that later.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Gotcha: RX has a number of buffering-like extension methods,
but you do need to be careful which one you choose based on the behaviour that
you want. Throttle() sounds ideal for my
use case, but Throttle ignores any updates that happen repeatedly within your
interval period. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h2>
<b>Task 4 – Using MEF to
Discover Connectable Services (Part 2)</b></h2>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ve created a ViewModel that, when given a list of IConnectable
objects, will subscribe to status updates in each. The flip side of this is the way in which we
tell MEF how to go about looking for assemblies. There are a number of ways to tell MEF how to
do this, but one of my requirements was to use a <i>plugins</i> approach. When the
app starts up it should look in a predefined folder for any “<i>extensions</i>” assemblies. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To achieve this I need to use a <i>DirectoryCatalog</i> passing in the path that I’m interested – MEF will
then look for any .DLL files (by default) and parse the types in each assembly. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
As a convenience my ServiceDashboard.Presentation and PricingServices
projects have a post-build event that copies the output into an <i>Extensions</i> folder in my WPF ServiceDashboard.UI
app (this makes it easier to run the solution from Visual Studio). Note, ServiceDashboard.UI does not have a
direct project reference to either:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span style="font-family: "Courier New"; font-size: 8.0pt; line-height: 107%;">xcopy "$(TargetDir)*.*" "$(SolutionDir)ServiceDashboard.UI\bin\$(ConfigurationName)\Extensions\"
/Y<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’m going to use a simple implementation of the MefBootstrapper
in conjunction with a basic Prism “shell”.
I’m only using the Prism <i>Shell</i>
as a convenient way to initialise MEF, and to load up a specific WPF Window are
runtime. Normally you’d use a Shell with
specific named regions, but that’s beyond the scope of this post.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
NB: you’ll need to run the following into Package Manager to
get the current Prism components from NuGet: <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<i>install-package Prism<br />
install-package Prism.MEFExtensions<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ve created a new WPF application, ServiceDashboard.UI,
into which I’ve add a class called AppBootStrapper <o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel.Composition;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.ComponentModel.Composition.Hosting;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.IO;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> System.Windows;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using</span><span style="font-family: Consolas;"> Microsoft.Practices.Prism.MefExtensions;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">AppBootStrapper</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MefBootstrapper</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> ConfigureAggregateCatalog()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">base</span><span style="font-family: Consolas; font-size: 10pt;">.ConfigureAggregateCatalog();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> AggregateCatalog.Catalogs.Add(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">AssemblyCatalog</span><span style="font-family: Consolas; font-size: 10pt;">(GetType().Assembly));<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> AddInPath = </span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"Extensions"</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> extensionsPath = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Path</span><span style="font-family: Consolas; font-size: 10pt;">.Combine(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">AppDomain</span><span style="font-family: Consolas; font-size: 10pt;">.CurrentDomain.BaseDirectory, AddInPath);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> AggregateCatalog.Catalogs.Add(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DirectoryCatalog</span><span style="font-family: Consolas; font-size: 10pt;">(extensionsPath));<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> InitializeShell()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Application</span><span style="font-family: Consolas; font-size: 10pt;">.Current.MainWindow = (</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Shell</span><span style="font-family: Consolas; font-size: 10pt;">)Shell;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Application</span><span style="font-family: Consolas; font-size: 10pt;">.Current.MainWindow.Show();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DependencyObject</span><span style="font-family: Consolas; font-size: 10pt;"> CreateShell()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> Container.GetExportedValue<</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Shell</span><span style="font-family: Consolas; font-size: 10pt;">>();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> ConfigureContainer()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">base</span><span style="font-family: Consolas; font-size: 10pt;">.ConfigureContainer();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Container.ComposeParts(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">this</span><span style="font-family: Consolas; font-size: 10pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<pre style="background: white;"><o:p> </o:p></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I won’t go into too much detail with MefBootstrapper (will
leave that for another post) - just the key methods that I need override.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h4>
<b>ConfigureAggregateCatalog()</b></h4>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Here we tell MEF where to find assembles that are resolved
using the Import/Export attributes. I’m
using <i>AggregateCatalog.Catalogs.Add(new
AssemblyCatalog(GetType().Assembly)) </i> to include the current running WPF app (as
it’s participating in part aggregation too).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
More importantly I’ve using <i>DirectoryCatalog</i> to tell MEF to look in an “Extensions” subfolder
for .DLL files:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> AddInPath = </span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"Extensions"</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> extensionsPath = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Path</span><span style="font-family: Consolas; font-size: 10pt;">.Combine(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">AppDomain</span><span style="font-family: Consolas; font-size: 10pt;">.CurrentDomain.BaseDirectory, AddInPath);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">AggregateCatalog.Catalogs.Add(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DirectoryCatalog</span><span style="font-family: Consolas; font-size: 10pt;">(extensionsPath));<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
As mentioned above, my two of projects have a post-build
event to copy build output to that folder.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h4>
<b>InitializeShell()</b></h4>
<div class="MsoNormal">
I’ve created a WPF Window called <i>Shell</i> (coming up shortly).
This method will get called once MEF has been configured correctly. I’m using it to make Shell the active window.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
That’s the basics of the AppBootStrapper covered, but I
still need to tell the WPF app to load and configure this boot strapper. So rather than set a specific start up Window,
I’ve overridden the <i>OnStartup</i> method
in App.xaml.cs:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">partial</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">App</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Application</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> OnStartup(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StartupEventArgs</span><span style="font-family: Consolas; font-size: 10pt;"> e)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: green; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">//DispatcherUnhandledException += (sender, args) => Log("Dispatcher Unhandled exception", args.Exception);</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">base</span><span style="font-family: Consolas; font-size: 10pt;">.OnStartup(e);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: green; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">//TaskScheduler.UnobservedTaskException += HandleUnObservedTask;</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: green; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">//AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Mouse</span><span style="font-family: Consolas; font-size: 10pt;">.OverrideCursor = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Cursors</span><span style="font-family: Consolas; font-size: 10pt;">.AppStarting;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> splashScreen = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">SplashScreen</span><span style="font-family: Consolas; font-size: 10pt;">(</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"/Assets/splash.jpg"</span><span style="font-family: Consolas; font-size: 10pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> splashScreen.Show(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">false</span><span style="font-family: Consolas; font-size: 10pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> bootstrapper = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">AppBootStrapper</span><span style="font-family: Consolas; font-size: 10pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> bootstrapper.Run();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Mouse</span><span style="font-family: Consolas; font-size: 10pt;">.OverrideCursor = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">null</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> splashScreen.Close(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">.FromSeconds(1));<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
NB: I’ve commented out the common exception handlers that I
usually attach to in a Release build. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
A splash screen .JPG resource is show using the Prism
SpashScreen object (this is loaded prior to the time consuming WPF
initialisation) and have guessed that it *might* take a second for MEF to
finish and my Shell screen to be visible.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
At the heart of the UI is the Shell.xaml. As stated earlier, this is not a lesson on
how to create a model Shell implementation, as such my Shell contains just a DataGrid
that is bound to the a <i>Connections</i>
property of my View Model<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Window</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Class</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="ServiceDashboard.UI.Shell"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="http://schemas.microsoft.com/winfx/2006/xaml/presentation"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="http://schemas.microsoft.com/winfx/2006/xaml"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">converters</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="clr-namespace:ServiceDashboard.UI.Converters"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">d</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="http://schemas.microsoft.com/expression/blend/2008"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">mc</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="http://schemas.openxmlformats.org/markup-compatibility/2006"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> xmlns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ui</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="clr-namespace:ServiceDashboard.UI"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> mc</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Ignorable</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="d"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> d</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataContext</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">d</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DesignInstance</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Type</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">=ui:ConnectablesStatusViewModelDesignTime}"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Title</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Shell"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Height</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="400"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Width</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="400"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> WindowStartupLocation</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="CenterScreen"</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Margin</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="2"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> WindowStyle</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="ToolWindow" ></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> ItemsSource</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Connections</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> AutoGenerateColumns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="False"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Margin</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="8"</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Padding</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="4"></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Resources</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">converters</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StatusToBrushConverter</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Key</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="statusToBrushConverter" /></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Resources</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Columns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Header</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Time"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Binding</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> TimeStamp</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">,</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> StringFormat</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">=hh</span><span style="font-family: Consolas; font-size: 10pt;">\\</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:mm</span><span style="font-family: Consolas; font-size: 10pt;">\\</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:ss</span><span style="font-family: Consolas; font-size: 10pt;">\\</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">.ff}"</span><span style="font-family: Consolas; font-size: 10pt;"><br />
</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> MinWidth</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="40"/></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Header</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Status"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Binding</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Status</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> MinWidth</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="90"></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn.ElementStyle</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Style</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> TargetType</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Type</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> TextBlock</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}"></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Setter</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Property</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Background"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Value</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Status</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">,</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Converter</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">={</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StaticResource</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> statusToBrushConverter</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}}"/></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Style</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn.ElementStyle</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGridTextColumn</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Header</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Description"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Binding</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Description</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> MinWidth</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="180"/></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Columns</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Window</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
I’ve created a Design Time instance of my View Model, <i>ConnectablesStatusViewModelDesignTime</i>. This makes it easier to verify Bindings at
design time:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectablesStatusViewModelDesignTime</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IConnectablesStatusViewModel</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IEnumerable</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IConnectableStatusItem</span><span style="font-family: Consolas; font-size: 10pt;">> Connections { </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">get</span><span style="font-family: Consolas; font-size: 10pt;">; </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">set</span><span style="font-family: Consolas; font-size: 10pt;">; }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
I’ve also created a class <i>StatusToBrushConverter</i>.cs
to convert the Connection Status to a known colour which is applied to the Status TextColumn’s <i>Background</i> property:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Resources</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">converters</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StatusToBrushConverter</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Key</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="statusToBrushConverter" /></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DataGrid.Resources</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"><</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Style</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> TargetType</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">x</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">:</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Type</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> TextBlock</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}"></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> <</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Setter</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Property</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="Background"</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Value</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">="{</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Status</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">,</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> Converter</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">={</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StaticResource</span><span style="color: red; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"> statusToBrushConverter</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">}}"/></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;"></</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Style</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">></span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">[</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ValueConversion</span><span style="font-family: Consolas; font-size: 10pt;">(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">typeof</span><span style="font-family: Consolas; font-size: 10pt;">(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">), </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">typeof</span><span style="font-family: Consolas; font-size: 10pt;">(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Color</span><span style="font-family: Consolas; font-size: 10pt;">))]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">StatusToBrushConverter</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IValueConverter</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> Convert(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> value, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Type</span><span style="font-family: Consolas; font-size: 10pt;"> targetType, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> parameter,<br />
</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">CultureInfo</span><span style="font-family: Consolas; font-size: 10pt;"> culture)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">if</span><span style="font-family: Consolas; font-size: 10pt;"> (! (value </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">is</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="font-family: Consolas; font-size: 10pt;">.DoNothing;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> status = (</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">)value;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">switch</span><span style="font-family: Consolas; font-size: 10pt;"> (status)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">case</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">.Connected:<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Brushes</span><span style="font-family: Consolas; font-size: 10pt;">.LightGreen;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">case</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">.Disconnected:<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Brushes</span><span style="font-family: Consolas; font-size: 10pt;">.Yellow;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">case</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">.Error:<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Brushes</span><span style="font-family: Consolas; font-size: 10pt;">.Red;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">case</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">ConnectionStatus</span><span style="font-family: Consolas; font-size: 10pt;">.Unknown:<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Brushes</span><span style="font-family: Consolas; font-size: 10pt;">.Gainsboro;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">default</span><span style="font-family: Consolas; font-size: 10pt;">:<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">DependencyProperty</span><span style="font-family: Consolas; font-size: 10pt;">.UnsetValue;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> ConvertBack(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> value, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Type</span><span style="font-family: Consolas; font-size: 10pt;"> targetType, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;"> parameter,<br />
</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">CultureInfo</span><span style="font-family: Consolas; font-size: 10pt;"> culture)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Binding</span><span style="font-family: Consolas; font-size: 10pt;">.DoNothing;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
I still need to register my Shell class with MEF, so I’ve
decorated Shell.xaml.cs with Export/Import attributes:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">[</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Export</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">partial</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Shell</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Window</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> Shell()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> InitializeComponent();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Import</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IConnectablesStatusViewModel</span><span style="font-family: Consolas; font-size: 10pt;"> ViewModel<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">set</span><span style="font-family: Consolas; font-size: 10pt;"> { DataContext = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">value</span><span style="font-family: Consolas; font-size: 10pt;">; }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
At runtime, MEF will look for an item in its catalog which
satisfies the <i>IConnectablesStatusViewModel</i>
Import/Export. In this case it will be a
shared concrete instance of <i>ConnectablesStatusViewModel</i>.
The Setter will use that to set the
Window’s DataContext – which the DataGrid is bound to.</div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<h2>
<b>Task 5 – Unit Testing
</b></h2>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
There’s a whole list of things that need to be unit tested,
but for this demo I’ll just concentrate on the ConnectablesStatusViewModel.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
I have a Unit Test project , ServiceDashboard.UnitTest, which
uses a couple of NuGet packages:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
FakeItEasy<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
NUnit<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
Rx-Testing<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
Rx-PlatformServices<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
In this project I have ConnectablesStatusViewModelTests (in
the <i>Presentation</i> folder). Because I’m using the Sample extension
method, I’ll need to make some changes to <i>ConnectablesStatusViewModel
</i>in order to gain control of time - remember that we’re buffering updates
into time slices, so I’ll need to make use of an IScheduler instance from the
RX concurrency namespace.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
In the unit tests I’m going to use a <i>TestScheduler</i> instance, in the real WPF application I’d use the <i>DispatcherScheduler.Current </i>instance<i> </i>as a scheduler.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
So back to <i>ConnectablesStatusViewModel</i>.
I’m going to add a new argument to the constructor - IScheduler scheduler and use
the Sample overload which takes an interval AND a scheduler instance:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> ConnectablesStatusViewModel(</span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables,
</span><span style="background: yellow; color: #2b91af; font-family: Consolas; mso-highlight: yellow;">IScheduler</span><span style="background: yellow; font-family: Consolas;"> scheduler</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connections = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">ConnectableStatusItem</span><span style="font-family: Consolas;">>();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">const</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">int</span><span style="font-family: Consolas;"> SamplePeriodInSeconds = 1;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> sampleInterval = </span><span style="color: #2b91af; font-family: Consolas;">TimeSpan</span><span style="font-family: Consolas;">.FromSeconds(SamplePeriodInSeconds);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">foreach</span><span style="font-family: Consolas;"> (</span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectable </span><span style="color: blue; font-family: Consolas;">in</span><span style="font-family: Consolas;"> connectables<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(lazy => lazy.Value)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .OrderBy(c => c.Description))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> statusItem = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectableStatusItem</span><span style="font-family: Consolas;">(connectable.Description);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> connections.Add(statusItem);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> subscription = connectable.Status<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <span style="background: yellow; mso-highlight: yellow;">.Sample(sampleInterval, scheduler)</span><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Subscribe(newStatus =><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.Status = newStatus;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusItem.TimeStamp = </span><span style="color: #2b91af; font-family: Consolas;">DateTime</span><span style="font-family: Consolas;">.Now.TimeOfDay;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> });<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> _disposables.Add(subscription);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> Connections = connections;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
By doing this, I’m asking RX to use a specific Scheduler to
run the sampling timer on. The <i>TestScheduler</i> allows me to move time
forwards to confirm that I’m only getting a specific number of updates per interval.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
As I’m using MEF and an ImportingConstructor attribute I ideally
need to expose a part that returns the DispatcherScheduler.Current for a named <i>Scheduler</i> export, for the time being I’ve
created a constructor overload which is used by MEF and keep the second constructor
which I’ll access via my unit tests.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="font-family: Consolas;">[</span><span style="color: #2b91af; font-family: Consolas;">ImportingConstructor</span><span style="font-family: Consolas;">]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> ConnectablesStatusViewModel([</span><span style="color: #2b91af; font-family: Consolas;">ImportMany</span><span style="font-family: Consolas;">] </span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> : </span><span style="color: blue; font-family: Consolas;">this</span><span style="font-family: Consolas;">(connectables, </span><span style="background: yellow; color: #2b91af; font-family: Consolas; mso-highlight: yellow;">DispatcherScheduler</span><span style="background: yellow; font-family: Consolas;">.Current</span><span style="font-family: Consolas;">)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{ }<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> ConnectablesStatusViewModel(</span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables, </span><span style="color: #2b91af; font-family: Consolas;">IScheduler</span><span style="font-family: Consolas;"> scheduler)<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
{….}<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
Back to the unit tests,
I’ve added a new test with the interesting title <i>MultipleConnectables_ConnectionsCountEqualsConnectablesCount</i>. My test asserts that the count of connectables
passed into the view model matches the count of the Connections property.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
I have a couple of static helper methods:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<i>CreateConnectable</i>():
creates a <i>fake</i> connectable instance based on IConnectable using FakeItEasy’s A.Fake
static method.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<i>CreateViewModel()</i>:
creates a concrete ConnectablesStatusViewModel instance with the specified List
of connectables and the TestScheduler.
Although I’m passing a TestScheduler, I’m not actually using that as
part of this test.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="font-family: Consolas;">[</span><span style="color: #2b91af; font-family: Consolas;">TestFixture</span><span style="font-family: Consolas;">]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectablesStatusViewModelTests</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> [</span><span style="color: #2b91af; font-family: Consolas;">Test</span><span style="font-family: Consolas;">]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> MultipleConnectables_ConnectionsCountEqualsConnectablesCount()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// ARR</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectables = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>(CreateConnectable),<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>(CreateConnectable),<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>(CreateConnectable)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> };<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// ACT</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> vm = CreateViewModel(connectables, </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">TestScheduler</span><span style="font-family: Consolas;">());<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// ASS</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Assert</span><span style="font-family: Consolas;">.That(vm.Connections.Count(), </span><span style="color: #2b91af; font-family: Consolas;">Is</span><span style="font-family: Consolas;">.EqualTo(connectables.Count));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
}<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectablesStatusViewModel</span><span style="font-family: Consolas;"> CreateViewModel(</span><span style="color: #2b91af; font-family: Consolas;">IEnumerable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>> connectables, </span><span style="color: #2b91af; font-family: Consolas;">IScheduler</span><span style="font-family: Consolas;"> scheduler)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">ConnectablesStatusViewModel</span><span style="font-family: Consolas;">(connectables, scheduler);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;"> CreateConnectable()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> CreateConnectable(</span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Subject</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;">>());<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">static</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;"> CreateConnectable(</span><span style="color: #2b91af; font-family: Consolas;">IObservable</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;">> subject)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectable = </span><span style="color: #2b91af; font-family: Consolas;">A</span><span style="font-family: Consolas;">.Fake<</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">A</span><span style="font-family: Consolas;">.CallTo(() => connectable.Status)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Returns(subject);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> connectable;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
My next test is more interesting. I create a single Connectable instance, but use
a Subject<ConnectionStatus> (statusSubject ) which allows me to explicitly
fire status changes.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
As there’s only one connectable instance, I retrieve the first
ConnectableStatusItem from the Connections property. From this I subscribe to changes in the Status
using my FromPropertyChangedPattern extension method, to increment a simple counter
when that property changes:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="font-family: Consolas;">connectionStatusItem.FromPropertyChangedPattern(item => item.Status)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Subscribe(_ => statusChangedCount++);<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<pre style="background: white;"><span style="font-family: Consolas;"> [</span><span style="color: #2b91af; font-family: Consolas;">Test</span><span style="font-family: Consolas;">]<o:p></o:p></span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> MultipleStatusChanges_FiresStatusPropertyChangedOncePerInterval()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// ARR</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> statusSubject = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Subject</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;">>();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectables = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Lazy</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">IConnectable</span><span style="font-family: Consolas;">>( () => CreateConnectable(statusSubject))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> };<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> scheduler = </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">TestScheduler</span><span style="font-family: Consolas;">();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> vm = CreateViewModel(connectables, scheduler);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> connectionStatusItem = vm.Connections.First();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> statusChangedCount = 0;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> connectionStatusItem.FromPropertyChangedPattern(item => item.Status)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Subscribe(_ => statusChangedCount++);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// ACT</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> testStatuses = </span><span style="color: #2b91af; font-family: Consolas;">Enum</span><span style="font-family: Consolas;">.GetValues(</span><span style="color: blue; font-family: Consolas;">typeof</span><span style="font-family: Consolas;">(</span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;">))<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Cast<</span><span style="color: #2b91af; font-family: Consolas;">ConnectionStatus</span><span style="font-family: Consolas;">>()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">for</span><span style="font-family: Consolas;"> (</span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> i = 0; i < 10; i++)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">foreach</span><span style="font-family: Consolas;"> (</span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> status </span><span style="color: blue; font-family: Consolas;">in</span><span style="font-family: Consolas;"> testStatuses)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> statusSubject.OnNext(status);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// verify that we've not yet had an property changes</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Assert</span><span style="font-family: Consolas;">.That(statusChangedCount, </span><span style="color: #2b91af; font-family: Consolas;">Is</span><span style="font-family: Consolas;">.EqualTo(0));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: green; font-family: Consolas;">// Wind the clock forward a bit</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> halfInterval = </span><span style="color: #2b91af; font-family: Consolas;">TimeSpan</span><span style="font-family: Consolas;">.FromSeconds(0.5).Ticks;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> scheduler.AdvanceBy(halfInterval);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Assert</span><span style="font-family: Consolas;">.That(statusChangedCount, </span><span style="color: #2b91af; font-family: Consolas;">Is</span><span style="font-family: Consolas;">.EqualTo(0));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> scheduler.AdvanceBy(halfInterval);<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">Assert</span><span style="font-family: Consolas;">.That(statusChangedCount, </span><span style="color: #2b91af; font-family: Consolas;">Is</span><span style="font-family: Consolas;">.EqualTo(1));<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
The For loop represents many status updates firing. Remember that we have a rule which states multiple
status updates should be buffered into once per second. Using the TestScheduler I can effectively
wind the clock to represent a change in time, using the AdvanceBy method. <o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
So I wind the clock forward by half a second and assert that
the status change event has not yet fired.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
Again I forward time by half a second, this gets us past our
sampling interval, no matter how many status update have fired, and I’ve only
received one property change.<o:p></o:p><br />
<br />
That's it. Source Code can be found here:<br />
<br /></div>
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2117074&authkey=AGRNRx0Tk_vXDqY" width="98"></iframe>Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-67273922791174367012015-02-10T12:48:00.002-08:002015-03-04T05:06:19.394-08:00How Slow is Repeatedly Parsing an Expression for INotifyPropertyChanged<div class="MsoNormal">
Introduced in .NET Framework v4.5 (as a C# 5.0 compiler
feature), the CallerMemberNameAttribute allows you to determine the method or
property name of the caller of a method.</div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
This comes in very handy if you’re adding logging code, or in
the case of with WPF you’re using models based on the INotifyPropertyChanged
interface, where you need to fire the PropertyChanged event passing along the
name of the property that’s changed.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
Prior to the availability of CallerMemberName, you had a
couple of choices when calling the PropertyChanged event: <o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
</div>
<ol>
<li>Pass a hard-coded string to identify the property name. <br />This has clear drawbacks – what if the property name changes/identifying code
dependent on the property?<br /> </li>
<li>Parsing an expression tree that represents a concrete class
property<br />Handy, because you’re referring to a class's property </li>
</ol>
<br />
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
There are plenty of examples available showing how to parse
an expression tree (see below), but be very careful, parsing an expression tree
is <b>very, very slow</b> compared to using
the CallerMemberName or passing in a string to identify the property. <o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br />
<a name='more'></a><br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<h3>
<b>Results</b></h3>
</div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b>If your application
can target v4.5 then, in terms of performance, it’s strongly recommended that
you replace any expression tree parsing with the CallerMemberNameAttribute</b>.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
The chart below shows how slow repeatedly parsing an
Expression in order to determine the bound property, can be, when compared to CallerMemberNameAttribute
or a string value:<o:p></o:p></div>
<br />
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5ULimYceBn_mK_utjg6W8UkXHivNUax71i6g-_E5ka3GxzBpN7uZszOJyysPuo2s4IWnx6PPQAAIVTtrQVlegBB7yiwyjjAysRey1ihuHK_RU_acB9WyW0S-BggStve_4qJGp/s1600/chart.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5ULimYceBn_mK_utjg6W8UkXHivNUax71i6g-_E5ka3GxzBpN7uZszOJyysPuo2s4IWnx6PPQAAIVTtrQVlegBB7yiwyjjAysRey1ihuHK_RU_acB9WyW0S-BggStve_4qJGp/s1600/chart.jpg" height="406" width="640" /></a></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<o:p><br /></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
In order to determine these numbers, I created a suite of tests,
that all performed the same task of firing a property change notification
change for a collection of 100 objects (each object having two properties
changed 200 times). <o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
The statistical 95<sup>th</sup> percentile number of
milliseconds for each test was calculated (rather than using a less accurate <i>average</i>) and plotted above.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184; width: 586px;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 148.0pt;" valign="top" width="197"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>Implementation<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 50.2pt;" valign="top" width="67"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>No. Of Objects<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>Properties to Change<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 99.2pt;" valign="top" width="132"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>No. of Times Each Property Changed<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>Time Taken Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 148.0pt;" valign="top" width="197"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
Expression
Parsing<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 50.2pt;" valign="top" width="67"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
100<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
2<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 99.2pt;" valign="top" width="132"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
200<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>238<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 148.0pt;" valign="top" width="197"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
CallerMemberNameAttribute<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 50.2pt;" valign="top" width="67"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
100<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
2<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 99.2pt;" valign="top" width="132"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
200<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>4<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 148.0pt;" valign="top" width="197"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
Hard
Coded String <o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 50.2pt;" valign="top" width="67"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
100<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
2<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 99.2pt;" valign="top" width="132"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
200<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 70.9pt;" valign="top" width="95"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
<b>4<o:p></o:p></b></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<o:p>
</o:p></div>
<div class="MsoNormal">
<o:p> </o:p>You can see that repeatedly parsing an Expression was more
than 50 times slower than the other two methods. </div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Clearly, if you only have a few objects to track or the
number of property change is equally low then this will not be such a problem,
but as the volumes increase you might notice a difference – and this is before
you factor in the time it takes for your observer action to complete. In my case I was simply incrementing a counter
to keep the action work to a minimum.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h3>
<b>Code Snippets</b></h3>
</div>
<div class="MsoNormal">
Before delving into the test code, I’ll summarise a few basic
snippets for the three standard INotifyPropertyChanged implementations.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h3>
<b>1. CallerMemberName</b></h3>
</div>
<div class="MsoNormal">
Using the CallerMemberName attribute to notify a change in
the Customer’s Name (in addition you’d usually check that the old property
value is different to the new incoming value before notifying the change)<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="color: blue;"><span style="font-family: Courier New, Courier, monospace;">public class Customer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(<b>[CallerMemberName]</b> string propName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
<b>OnPropertyChanged();</b>
}
}
}</span><span style="background-color: transparent;"> </span></span></pre>
</pre>
<div class="MsoNormal">
<h3>
<b>2. Hard Coded
Property Name Strings </b></h3>
</div>
<div class="MsoNormal">
Contrast this with the older style of passing along a string
to identify the property. <o:p></o:p></div>
<div class="MsoNormal">
It’s simple, it works, but you have no easy way of checking
if the name of bound property changes or the impact of changing that name or
how it’s really used – a potential maintenance nightmare.<o:p></o:p></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="color: blue; font-family: Courier New, Courier, monospace;">public class Customer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
<b>OnPropertyChanged("Name");</b>
}
}
}</span></pre>
</pre>
<div class="MsoNormal">
<h3>
<b>3. Expression Parsing </b></h3>
</div>
<div class="MsoNormal">
You get the benefits of compile-time checking and it’s easy to
determine property usage in your code…but it can be very slow.<o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Courier New, Courier, monospace;">public class Customer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
<b>OnPropertyChanged(() => Name);</b>
}
}
protected void OnPropertyChanged<T>(<b>Expression<Func<T>> expression</b>)
{
var handler = PropertyChanged;
if (handler != null)
{
var propName = GetPropertyNameFromExpression(expression);
handler(this, new PropertyChangedEventArgs(propName));
}
}
<b>private static string GetPropertyNameFromExpression<T>(Expression<Func<T>> exp)
{
var memberExpression = exp.Body as MemberExpression
?? ((UnaryExpression)exp.Body).Operand as MemberExpression;
if (memberExpression == null)
throw new ArgumentException(
String.Format("[{0}] is not a member expression", exp.Body));
return memberExpression.Member.Name;
}</b>
}</span><span style="background-color: transparent;"> </span></pre>
<div class="MsoNormal">
<h3>
<b>Test Cases</b></h3>
</div>
<div class="MsoNormal">
In order to run the tests, I prefer to use the NUnit
framework to create the tests and then run them using Resharper (in a handy single
click).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Using the Package Manager console, you’ll need to run <b><i>Install-Package
NUnit</i></b> to get NUnit downloaded from NuGet and referenced in your
project.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I have a general purpose base class that I often use when
creating timed test code, TimedActionBase.cs.
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">using System;
using System.Collections.Generic;
using System.Diagnostics;
using NUnit.Framework;
[TestFixture]
public abstract class TimedActionBase
{
protected abstract void TimedAction();
…</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
There is an abstract void method, <i>TimedAction</i>(), which contains the code that I want to measure and two
public methods that NUnit can see:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">[Test]
public void SinglePassRunner()
{
TimedActionRunner();
}
[Test]
public void MultiPassRunner()
{
const int NoOfPasses = 180;
for (var i = 0; i < NoOfPasses; i ++)
{
TimedActionRunner();
}
}</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
TimedActionRunner is the main controlling method,
responsible for timing the method under test and generating the output
statistics via - CalculateStatistics()<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">private void TimedActionRunner()
{
const int DefaultTestCycles = 100;
// Prerun TimedAction method in case there's any JIT overhead
TimedAction();
_durations.Clear();
for (var cycle = 1; cycle <= DefaultTestCycles; cycle++)
{
_timer.Start();
TimedAction();
_timer.Stop();
var duration = _timer.Elapsed;
_durations.Add(duration);
_timer.Reset();
}
CalculateStatistics();
}</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In each of the performance test, I’m testing a specific
implementation of a Car class (CarBase)<o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public abstract class CarBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected CarBase(int id)
{
Id = id;
}
public int Id { get; private set; }
public abstract string Name { get; set; }
public abstract int Speed { get; set; }
public abstract int Mileage { get; set; }
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The only difference between each of the tests is how <span style="font-family: Consolas;">OnPropertyChanged</span> is called (it
needs a string property in all cases).<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To create the actual tests I extend the TimedActionBase class
further with a NotifyTestBase class.
This serves as the core for each notification test. </div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="background-color: transparent;"><span style="color: blue; font-family: Consolas;">public abstract class NotifyTestBase : TimedActionBase
{
static class TestParams
{
public const int NumberOfCars = 100;
public const int NumberOfLaps = 200;
public const int MileageIncrement = 2;
public const int SpeedIncrement = 3;
}
private readonly IEnumerable<CarBase> _cars;
private int _propertyChangedCount;
protected NotifyTestBase()
{
_cars = Enumerable
.Range(1, TestParams.NumberOfCars)
.Select(i =>
{
var car = CreateCar(i);
car.PropertyChanged += (sender, args) => _propertyChangedCount ++;
return car;
})
.ToList();
}
protected override void TimedAction()
{
for (var i = 0; i < TestParams.NumberOfLaps; i++)
{
foreach (var car in _cars)
{
car.Mileage += TestParams.MileageIncrement;
car.Speed += TestParams.SpeedIncrement;
}
}
}
protected abstract CarBase CreateCar(int id);
}</span></span></pre>
<div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You’ll see that NotifyTestBase overrides the base class’s TimedAction()
method. It ensures that for each test, the same number of objects are modified
and the same number of times between each CarBase implementation by overriding
the factory method <span style="color: #2b91af; font-family: Consolas;">CarBase</span><span style="font-family: Consolas;"> CreateCar()</span>.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
All that’s required for each scenario is to create a
concrete implementation of CarBase and have a new instance of that type
returned in the test by overriding <i>CreateCar(int
i).<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Taking CallerMemberName as the first example, I’ll extend a
CarBase with a new class <i>NotifyByCallerMemberNameCar</i>:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public class NotifyByCallerMemberNameCar : CarBase
{
public NotifyByCallerMemberNameCar(int id) : base(id)
{}
private string _name;
public override string Name
{
get { return _name; }
set
{
SetAndRaiseIfChanged(ref _name, value);
}
}
private int _speed;
public override int Speed
{
get { return _speed; }
set
{
SetAndRaiseIfChanged(ref _speed, value);
}
}
private int _mileage;
public override int Mileage
{
get { return _mileage; }
set
{
SetAndRaiseIfChanged(ref _mileage, value);
}
}
private void SetAndRaiseIfChanged<T>(ref T backingField, T newValue, [CallerMemberName]string propName = null)
{
if (EqualityComparer<T>.Default.Equals(backingField, newValue))
{
return;
}
backingField = newValue;
OnPropertyChanged(propName);
}
}</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You can see that each of the property sets calls SetAndRaiseIfChanged
– no need to pass in the property name as the <i>propName</i> string is decorated with the [CallerMemberName] attribute.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In order to test this I need to create test class that
extends NotifyTestBase, with a method that overrides <i>CreateCar</i> to return the concrete NotifyByCallerMemberNameCar class:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public class NotifyByCallerMemberNameTest : NotifyTestBase
{
protected override CarBase CreateCar(int i)
{
return new NotifyByCallerMemberNameCar(i);
}
}</span><span style="font-family: Consolas;"> </span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The NotifyByExpressionTest looks similar, we return a NotifyByExpressionCar
instance, with each property set making use of an Expression:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public class NotifyByExpressionTest : NotifyTestBase
{
protected override CarBase CreateCar(int i)
{
return new NotifyByExpressionCar(i);
}
}
public class NotifyByExpressionCar : CarBase
{
public NotifyByExpressionCar(int id) : base(id)
{}
private string _name;
public override string Name
{
get { return _name; }
set
{
SetAndRaiseIfChanged(ref _name, value, () => Name);
}
}
private int _speed;
public override int Speed
{
get { return _speed; }
set
{
SetAndRaiseIfChanged(ref _speed, value, () => Speed);
}
}
private int _mileage;
public override int Mileage
{
get { return _mileage; }
set
{
SetAndRaiseIfChanged(ref _mileage, value, () => Mileage);
}
}
protected void SetAndRaiseIfChanged<T>(ref T backingField, T newValue,
Expression<Func<T>> expression)
{
if (EqualityComparer<T>.Default.Equals(backingField, newValue))
{
return;
}
var propName = GetPropertyNameFromExpression(expression);
backingField = newValue;
OnPropertyChanged(propName);
}
private static string GetPropertyNameFromExpression<T>(
Expression<Func<T>> expression)
{
var memberExpression = expression.Body as MemberExpression
?? ((UnaryExpression)expression.Body).Operand as MemberExpression;
if (memberExpression == null)
throw new ArgumentException(String.Format("[{0}] is not a member expression", expression.Body));
return memberExpression.Member.Name;
}
}</span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
and finally NotifyByStringTest - for the hard-coded string property<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">public class NotifyByStringTest : NotifyTestBase
{
protected override CarBase CreateCar(int i)
{
return new NotifyByStringCar(i);
}
}
public class NotifyByStringCar : CarBase
{
public NotifyByStringCar(int id) : base(id)
{}
private string _name;
public override string Name
{
get { return _name; }
set
{
SetAndRaiseIfChanged(ref _name, value, "Name");
}
}
private int _speed;
public override int Speed
{
get { return _speed; }
set
{
SetAndRaiseIfChanged(ref _speed, value, "Speed");
}
}
private int _mileage;
public override int Mileage
{
get { return _mileage; }
set
{
SetAndRaiseIfChanged(ref _mileage, value, "Mileage");
}
}
protected void SetAndRaiseIfChanged<T>(ref T backingField, T newValue, string propName)
{
if (EqualityComparer<T>.Default.Equals(backingField, newValue))
{
return;
}
backingField = newValue;
OnPropertyChanged(propName);
}
}</span>
</pre>
<div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You can download the full test solution from here.<o:p></o:p></div>
<div>
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2117004&authkey=AJ4pFvAm7_ZyHCw" width="200"></iframe>
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h3>
<b>Further Information </b></h3>
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I was interested in finding out how the use of CallerMemberNameAttribute
affects my assemblies. It turns out CallerMemberNameAttribute
is part of the System.Runtime.CompilerServices namespace:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="margin-left: 36.0pt;">
<i>The System.Runtime.CompilerServices namespace provides functionality
for compiler writers who use managed code to specify attributes in metadata
that affect the run-time behavior of the common language runtime. This namespace is primarily for compiler
writers<o:p></o:p></i></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Normally you use Reflection to get hold of types decorated
with an attribute at runtime, but the C# 5.0 compiler looks for this attribute
when compiling your code.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I used ILDASM, Microsoft’s MSIL dissembler, to examine assembly
that was created. It’s interesting to note
that the property sets created using CallerMemberName AND a string constant are
identical:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEholmB7Y85MjKuD4aA44i_J-pyEnL_12pRXLuZ7V69ymfUNRdmJaKEnTQ4riUr_RRWweQum7Uh_dhnHmtn-Ed92k7SAr3owNWQSSsNdXlp7tkj5ofEsygQGragE8d_4vdr22o5E/s1600/ildasm.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEholmB7Y85MjKuD4aA44i_J-pyEnL_12pRXLuZ7V69ymfUNRdmJaKEnTQ4riUr_RRWweQum7Uh_dhnHmtn-Ed92k7SAr3owNWQSSsNdXlp7tkj5ofEsygQGragE8d_4vdr22o5E/s1600/ildasm.jpg" height="144" width="640" /></a></div>
<div class="MsoNormal">
<br /></div>
</div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-58835247448624619792015-02-08T12:34:00.001-08:002015-03-04T05:10:08.683-08:00Improving Struct Performance By Overriding GetHashCode()<div class="MsoNormal">
In an <a href="http://jason-hales.blogspot.co.uk/2015/02/structs-improving-performance-with.html" target="_blank">earlier post</a>, I discussed the pitfall of using structs
in C# to store data and failing to override the Equals method. In this post I’ll look at the GetHashCode method
and how not overriding the default implementation can have a drastic effect on
the performance of a C# application.<o:p></o:p><br />
<br /></div>
<div class="MsoNormal">
I was asked by a client to investigate the performance of a C# WPF application; users were concerned that performance across certain areas just didn’t feel as fast as it ought to be – particularly during busy times of a trading day. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
After analysing the code and using an automated profile application (<a href="http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/" target="_blank">ANTS </a>by Red Gate) I narrowed down the bottle neck to a struct had been used to store stock market prices.</div>
<div class="MsoNormal">
<br />
<a name='more'></a><br /></div>
<h3>
Summary of Results (Bottom Line)</h3>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you don’t want to read how these numbers were created, then the bottom line is, if you are using structs to store data and these structs regularly have their Equals or GetHashCode default methods called - either directly or indirectly (by storing in a Dictionary), then in terms of performance you should override the methods and implement your own Equals and GetHashCode methods.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you’re using a struct and you do not override the Equals or GetHashCode methods, then you’re deferring down to the default implementations. These are shockingly slow, as they use Reflection to look at the fields defined in your struct.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The chart below shows (times in milliseconds) how slow it can be by repeatedly calling GetHashCode on a struct that does/does not have the GetHashCode method overridden.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt46zTRnUTa8rOSCsxzKwYZuqHSeY8azGR3r6DYWeErbKBqamMWSh3AjcberYf96y2u6uoNIr3ry3CpklEv_YIzrI5efOcTw494GNUkjOURKia5Giw-E5RaKhbxO_1eKWvXVry/s1600/gethashcode.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt46zTRnUTa8rOSCsxzKwYZuqHSeY8azGR3r6DYWeErbKBqamMWSh3AjcberYf96y2u6uoNIr3ry3CpklEv_YIzrI5efOcTw494GNUkjOURKia5Giw-E5RaKhbxO_1eKWvXVry/s1600/gethashcode.jpg" height="426" width="640" /></a></div>
<br /></div>
<div class="MsoNormal">
<br /></div>
<h3>
Use of Class Versus Struct To Store Data</h3>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Commonly known facts:</div>
<div class="MsoNormal">
</div>
<ul>
<li>Class instances get allocate on the heap</li>
<li>Class instances get via a copied pointer reference (size is 4/8 bytes for 32/64 bit process)</li>
<li>Class instances have additional overhead of 8/16 bytes for 32/64 bit process (in fact .NET aligns objects to the minimum of 12/24 bytes object size for 32/64 bit processes)</li>
<li>Structs have no additional overhead, the amount of memory is the sum of all fields in the struct</li>
<li>Structs declared in a class exist on the heap (not the stack!)</li>
<li>Structs, by default, when passed to a method are copies of the original struct (a byte-by-byte copy), so any changes made to the struct from inside the method are local to that method</li>
</ul>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The original application developer used a struct to store minute-by-minute stock market pricing data, rather than a class. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In addition to using structs to store the market data info, various supporting calculations required value equality comparison and were stored in Dictionary based collections, hence a large of calls to Equals and GetHashCode</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Unfortunately, these structs did not to override the GetHashCode and Equals methods, therefore using .NET’s default values, which resulted in some rather surprising performance issues.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Take a look at my <a href="http://jason-hales.blogspot.co.uk/2015/02/structs-improving-performance-with.html" target="_blank">Equals </a>post for further details of Blittable and Non-Blittable Types .</div>
<div class="MsoNormal">
<br /></div>
<h3>
Test Code Generate Results</h3>
<div class="MsoNormal">
I’m going to walk through a selection of code snippets to show how much of a performance difference you can see by implementing GetHashCode.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
As part of my profiling samples, I’ve created a base class (TimedActionBase) that, when used in conjunction with NUnit and Resharper, makes it really easy to run snippets of test code with a single mouse click and provide a statistical average for the area under test. You’ll find a link to the code at the bottom of this article. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You won’t need Resharper to compile the test code, but you’ll need to run Install-Package NUnit using the Package Manager Console to download and install NUnit from NuGet.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ll just summarise the base test class here, as it’s the actual tests that are more important. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Each test class derives from TimedActionBase and implements the TimedAction() method. This method gets called once initially (unloading any timed JIT overhead) and then is called a set number of times, with a percentile calculation used to provide a meaningful idea of how long the method takes to execute. I can then run SinglePassRunner() if a want a single timed pass (this calls the test method 100 times), or, in order to build up the data for the graphs MultiPassRunner(): </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System.Collections.Generic;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System.Diagnostics;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> NUnit.Framework;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">[</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TestFixture</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">abstract</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">> _durations = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">>();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Stopwatch</span><span style="font-family: Consolas; font-size: 10pt;"> _timer = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Stopwatch</span><span style="font-family: Consolas; font-size: 10pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span><span style="font-family: Consolas; font-size: 10pt;"> _headerLogged;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">abstract</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Test</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> SinglePassRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Test</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> MultiPassRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> NoOfPasses = 180;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">for</span><span style="font-family: Consolas; font-size: 10pt;"> (</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> i = 0; i < NoOfPasses; i ++)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> DefaultTestCycles = 100;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: green; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">// Prerun TimedAction method in case there's any JIT overhead</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _durations.Clear();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">for</span><span style="font-family: Consolas; font-size: 10pt;"> (</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> cycle = 1; cycle <= DefaultTestCycles; cycle++)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Start();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Stop();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> duration = _timer.Elapsed;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _durations.Add(duration);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Reset();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> CalculateStatistics();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> CalculateStatistics()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> average = _durations.AverageTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> min = _durations.MinTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> max = _durations.MaxTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> deviation = _durations.Deviation();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">double</span><span style="font-family: Consolas; font-size: 10pt;"> Percentile = 95D;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> percentile = _durations.Percentile(Percentile);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> header = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Empty;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">if</span><span style="font-family: Consolas; font-size: 10pt;"> (!_headerLogged)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _headerLogged = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">true</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> header = </span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"Timings(ms) \tPercentile\tAverage\tMinimum\tMaxmimum\tDeviation\n"</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Log(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Concat(header, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Join(</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"\t"</span><span style="font-family: Consolas; font-size: 10pt;">, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;">[]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> GetType().Name,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(percentile), FormatTimeSpan(average),<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(min), FormatTimeSpan(max),<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(deviation)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })));<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">static</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;"> span)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> span.TotalMilliseconds.ToString(</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"N0"</span><span style="font-family: Consolas; font-size: 10pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">static</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> Log(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> format, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">params</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;">[] args)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Console</span><span style="font-family: Consolas; font-size: 10pt;">.WriteLine(format, args);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To compare the performance of different implementations, all of the sample tests below follow the same basic steps:</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<ol>
<li>Define a struct with slightly different implementation under test (eg, with or without GetHashCode)</li>
<li>Create a List containing 50,000 structs</li>
<li>Walk through the list calling the GetHashCode method and calculate how long that timed operation takes to complete</li>
</ol>
<br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Even if you aren't directly calling GetHashCode in your code, it is used by .NET when you use the objects in any of the Dictionary based collections.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>1. Not Overriding GetHashCode()
With Non-Blittable Fields (Slowest Speed)<o:p></o:p></b></div>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>GetHashCode Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
No<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
70<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">SlowNonBlittableWithoutGetHashCodeTest</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">TimedActionBase</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">struct</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">MyStruct</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: #2b91af; font-family: Consolas;">Decimal</span></b><b><span style="font-family: Consolas;"> Field1;<o:p></o:p></span></b></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: #2b91af; font-family: Consolas;">Decimal</span></b><b><span style="font-family: Consolas;"> Field2;<o:p></o:p></span></b></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: #2b91af; font-family: Consolas;">Decimal</span></b><b><span style="font-family: Consolas;"> Field3;<o:p></o:p></span></b></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">readonly</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">MyStruct</span><span style="font-family: Consolas;">> _items = </span><span style="color: #2b91af; font-family: Consolas;">Enumerable</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas;">GetHashCodeTestParams</span><span style="font-family: Consolas;">.NumberOfItems)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(n => </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">MyStruct</span><span style="font-family: Consolas;">())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">override</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> TimedAction()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> hashcodes = _items<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(item => item.GetHashCode())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>2. Not Overriding GetHashCode()
With Blittable Fields (Medium)<o:p></o:p></b></div>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>GetHashCode Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
No<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
18<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">MediumBlittableWithoutGetHashCodeTest</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">TimedActionBase</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">struct</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">BlittableStruct</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">int</span></b><b><span style="font-family: Consolas;"> Field1;<o:p></o:p></span></b></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">int</span></b><b><span style="font-family: Consolas;"> Field2;<o:p></o:p></span></b></pre>
<pre style="background: white;"><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">public</span></b><b><span style="font-family: Consolas;"> </span></b><b><span style="color: blue; font-family: Consolas;">int</span></b><b><span style="font-family: Consolas;"> Field3;<o:p></o:p></span></b></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">readonly</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">BlittableStruct</span><span style="font-family: Consolas;">> _items = </span><span style="color: #2b91af; font-family: Consolas;">Enumerable</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas;">GetHashCodeTestParams</span><span style="font-family: Consolas;">.NumberOfItems)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(n => </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">BlittableStruct</span><span style="font-family: Consolas;">())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">override</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> TimedAction()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> hashcodes = _items<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(item => item.GetHashCode())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>3. Overriding GetHashCode()
With Blittable Fields (Fastest)<o:p></o:p></b></div>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>GetHashCode Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 132.95pt;" valign="top" width="177"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
Yes<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
9<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">class</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">FastWithGetHashCodeTest</span><span style="font-family: Consolas;"> : </span><span style="color: #2b91af; font-family: Consolas;">TimedActionBase</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">{<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">struct</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">StructWithGetHashcode</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">override</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">int</span><span style="font-family: Consolas;"> GetHashCode()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">return</span><span style="font-family: Consolas;"> Field1 ^ Field2 ^ Field3;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">int</span><span style="font-family: Consolas;"> Field1;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">int</span><span style="font-family: Consolas;"> Field2;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">int</span><span style="font-family: Consolas;"> Field3;<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">private</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">readonly</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">List</span><span style="font-family: Consolas;"><</span><span style="color: #2b91af; font-family: Consolas;">StructWithGetHashcode</span><span style="font-family: Consolas;">> _items = </span><span style="color: #2b91af; font-family: Consolas;">Enumerable</span><span style="font-family: Consolas;"><o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas;">GetHashCodeTestParams</span><span style="font-family: Consolas;">.NumberOfItems)<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(n => </span><span style="color: blue; font-family: Consolas;">new</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">StructWithGetHashcode</span><span style="font-family: Consolas;">())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">override</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> TimedAction()<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> {<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">var</span><span style="font-family: Consolas;"> hashcodes = _items<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .Select(item => item.GetHashCode())<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> .ToList();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: Consolas;">}<o:p></o:p></span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
The numbers are very surprising.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
You can view similar results for the <a href="http://jason-hales.blogspot.co.uk/2015/02/structs-improving-performance-with.html">Equals</a>
performance test post.<o:p></o:p><br />
<br /></div>
Source Code:<br />
<br />
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2116997&authkey=AHi17YC2OFp6XLc" width="98"></iframe>Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-624813446689599402015-02-08T10:43:00.001-08:002015-03-04T05:07:08.991-08:00Improving Struct Performance By Overriding Equals()<div class="MsoNormal">
I’ve spent a few days analysing the performance of a C# WPF
application. The client’s users were
concerned that performance across certain areas just didn’t feel as fast as it
ought to be – particularly during busy times of a trading day. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
That might sound pretty vague, but often as developers that’s
all we get.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
There are plenty of resources available that go into a great
level of detail on the best practices to follow when designing apps. It helps to understand what .NET does under
the hood, in many aspects, but it’s important to remember that there are many
layers in which subtle coding designs can negatively affect performance.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’m going to describe some interesting implementation
details which I discovered were the cause of performance issues that the users
were complaining about. This particular blog
covers structs and how slow Equals calls can be – please see my other blog
entry for similar results found by examining the default GetHashCode
implementation.<o:p></o:p></div>
<div class="MsoNormal">
<br />
<a name='more'></a><br /></div>
<h3>
<b>Summary of Results (Bottom
Line)</b></h3>
<div class="MsoNormal">
<b><br /></b></div>
<div class="MsoNormal">
If you don’t want to read how these numbers were created,
then <b>the bottom line </b>is, if you are
using structs to store data and these structs regularly have their Equals or
GetHashCode default methods called - either directly or indirectly (by storing
in a Dictionary), then <b>in terms of
performance you should override the methods and implement your own Equals and
GetHashCode methods</b>.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you’re using a struct and you do not override the Equals or
GetHashCode methods, then you’re deferring down to the default
implementations. These are shockingly
slow, as they use Reflection to look at the fields defined in your struct.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The chart below shows (times in milliseconds) how slow it
can be by repeatedly calling <b>Equals</b>
on a struct that does/does not have the Equals method overridden.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUwKA0WOS6rNffuHCXIrVnHtaM91r-y35VnzQD9Kx1pE_-_xAC8E3kZ5ABxdlm5ti2movXBtlTsmZOjcEBEOKCPFqk3y5UCYi9WkEXmLqqC3oyYW083zjgYRVqEOMCT3BPdJq_/s1600/equalschart.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUwKA0WOS6rNffuHCXIrVnHtaM91r-y35VnzQD9Kx1pE_-_xAC8E3kZ5ABxdlm5ti2movXBtlTsmZOjcEBEOKCPFqk3y5UCYi9WkEXmLqqC3oyYW083zjgYRVqEOMCT3BPdJq_/s1600/equalschart.jpg" height="408" width="640" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<v:shapetype coordsize="21600,21600" filled="f" id="_x0000_t75" o:preferrelative="t" o:spt="75" path="m@4@5l@4@11@9@11@9@5xe" stroked="f">
<v:stroke joinstyle="miter">
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0">
<v:f eqn="sum @0 1 0">
<v:f eqn="sum 0 0 @1">
<v:f eqn="prod @2 1 2">
<v:f eqn="prod @3 21600 pixelWidth">
<v:f eqn="prod @3 21600 pixelHeight">
<v:f eqn="sum @0 0 1">
<v:f eqn="prod @6 1 2">
<v:f eqn="prod @7 21600 pixelWidth">
<v:f eqn="sum @8 21600 0">
<v:f eqn="prod @7 21600 pixelHeight">
<v:f eqn="sum @10 21600 0">
</v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:formulas>
<v:path gradientshapeok="t" o:connecttype="rect" o:extrusionok="f">
<o:lock aspectratio="t" v:ext="edit">
</o:lock></v:path></v:stroke></v:shapetype><v:shape id="Chart_x0020_1" o:gfxdata="UEsDBBQABgAIAAAAIQAPPDsnOAEAAHsDAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbKyTy07DMBBF
90j8g+UtStyyQAg17YLHEliUDzD2pLHwSx63tH/PxEmRQAW1iE1e9txzPLFni62zbAMJTfANn9YT
zsCroI1fNfxl+VBdc4ZZei1t8NDwHSBfzM/PZstdBGRU7bHhXc7xRghUHTiJdYjgaaQNyclMr2kl
olRvcgXicjK5Eir4DD5Xuc/g89kdtHJtM7vf0ufBJIFFzm6HiT2r4TJGa5TMZCo2Xn+jVCOhpsoy
BzsT8YI0uDhI6Ed+Box1T9SaZDSwZ5nyo3SkIZQ18TXIpIVO8p0ahfuHaf176AHr0LZGgQ5q7agj
9Zi41/4drzqSQlFu/4Z2ti6BJylg3lk4RsFhNax4gJS6k0gq2JDwD6ihsPc8jpdpK4Mo12NoX7f8
uMjP31pi9lxRjs78AwAA//8DAFBLAwQUAAYACAAAACEArTA/8cEAAAAyAQAACwAAAF9yZWxzLy5y
ZWxzhI/NCsIwEITvgu8Q9m7TehCRpr2I4FX0AdZk2wbbJGTj39ubi6AgeJtl2G9m6vYxjeJGka13
CqqiBEFOe2Ndr+B03C3WIDihMzh6RwqexNA281l9oBFTfuLBBhaZ4ljBkFLYSMl6oAm58IFcdjof
J0z5jL0MqC/Yk1yW5UrGTwY0X0yxNwri3lQgjs+Qk/+zfddZTVuvrxO59CNCmoj3vCwjMfaUFOjR
hrPHaN4Wv0VV5OYgm1p+LW1eAAAA//8DAFBLAwQUAAYACAAAACEALx0dV3gBAAAZBAAAHwAAAGNs
aXBib2FyZC9kcmF3aW5ncy9kcmF3aW5nMS54bWykU8tOwzAQvCPxD5bvNAltKURNe2gp4gKVgA9Y
Oc5DxHZkuyH9e9ZuUEJBArUXy157Z2d21vNlKyrScG1KJRMajUJKuGQqLWWe0LfXzdUtJcaCTKFS
kid0zw1dLi4v5hDnGuqiZAQRpIkhoYW1dRwEhhVcgBmpmku8y5QWYPGo8yDV8IHIogquw/AmEFBK
uuih1mCB7HR5AlSl2DtPVyAbMAhZsXgY6ThW7HxkiGXzoOuXeqsdc/bUbDUp04Ri5yQIbBENuovu
GR6Do6y8B2gzLdx7lWWk9Sh7t3oM3lrCMDidjaNphAUY3k3C8WR2171gxfMveay4/yMTCR0K42ZA
prN0o1GII+WkepN95Fhx9KV4VYC2JBoIHyb1+odRB9+VG2zPmADmSKD1LPa7zvJTHD8gdQD6PyOD
5pWMrxXbCS7tYbg1r8DirzJFWRtKdOyGRD+mvk2+7b4dTrEfkEEzzpmJ7872HccaR59iWPUHi08A
AAD//wMAUEsDBBQABgAIAAAAIQDH6wo6VwEAAJ4CAAAmAAAAY2xpcGJvYXJkL2NoYXJ0cy9fcmVs
cy9jaGFydDEueG1sLnJlbHOskl1PwjAUhu9N/A9LEy5dt5EYQxhEJyoXRCJw18TU7uwDu57ZU8z4
9xYNRgzEGy/79T5vn3Y47hodvIOlGk3K4jBiARiFeW3KlK2WdxdXLCAnTS41GkjZFoiNR+dnwyfQ
0vlDVNUtBT7FUMoq59oB56QqaCSF2ILxKwXaRjo/tCVvpXqVJfAkii65/ZnBRgeZwTRPmZ3mfRYs
t60n/52NRVEruEW1acC4IwiOGh5f1qCcD5W2BJeyotbgK/NsIFbkPYi1JDTPURSJfRKJucXSyqbx
VsSNxrKXRHMkR2JWk8p6SV/MwX5e0yhYeiFi4exGuXtwD5KqDHO4NvnkbSM17WJJeKfeSKep21eZ
+U0pm3QOrJGa8eM6khM6mlpZJCxcqLDhXya8gTg+lMxVJa3LUKNduK2GPTxlajdHcejf6xQ7/g/2
LyztWnxT+cGvGn0AAAD//wMAUEsDBBQABgAIAAAAIQBnA+6GzgAAAKwBAAAqAAAAY2xpcGJvYXJk
L2RyYXdpbmdzL19yZWxzL2RyYXdpbmcxLnhtbC5yZWxzrJDNasMwDIDvg72D0X1W0sMYo04vpdDr
6B5AOMoPTWxjqWV9+5kWxgKFXnqRkIQ+fWi9+Zknc+YsYwwOaluB4eBjO4bewfdh9/YBRpRCS1MM
7ODCApvm9WX9xRNpWZJhTGIKJYiDQTV9IoofeCaxMXEoky7mmbSUucdE/kg946qq3jH/Z0CzYJp9
6yDv2xWYwyWVy4/ZsetGz9voTzMHvXMCtXhxAVLuWR1Ye+vcYm2LK+B9jfqZGn6grAuNa0fwmv48
cPHj5hcAAP//AwBQSwMEFAAGAAgAAAAhALY7BCJUBgAACxoAABoAAABjbGlwYm9hcmQvdGhlbWUv
dGhlbWUxLnhtbOxZS28bNxC+F+h/WOy9sd6KjciBrUfcxk6CSEmRI6WldhlzlwuSsqNbkRwLFCia
Fj00QG89FG0DJEAv6a9xm6JNgfyFDrkPkRJVO0YKGEEswNid/WY4nJn9huReufogpt4R5oKwpONX
L1V8DycTFpAk7Ph3RoOPLvuekCgJEGUJ7vhzLPyr2x9+cAVtTShJxwzxYBThGHtgKBFbqONHUqZb
GxtiAmIkLrEUJ/BsyniMJNzycCPg6BgGiOlGrVJpbcSIJP42WJTKUJ/Cv0QKJZhQPlRmsJegGEa/
OZ2SCdbY4LCqEGIuupR7R4h2fLAZsOMRfiB9jyIh4UHHr+g/f2P7ygbaypWoXKNr6A30X66XKwSH
NT0mD8floI1Gs9HaKe1rAJWruH673+q3SnsagCYTmGnmi2mzubu522vmWAOUXTps99q9etXCG/br
Kz7vNNXPwmtQZr+xgh8MuhBFC69BGb65gm802rVuw8JrUIZvreDblZ1eo23hNSiiJDlcQVearXq3
mG0JmTK654RvNhuDdi03vkBBNZTVpYaYskSuq7UY3Wd8AAAFpEiSxJPzFE/RBGqyiygZc+LtkzCC
wktRwgSIK7XKoFKH/+rX0Fc6ImgLI0Nb+QWeiBWR8scTE05S2fE/Aau+AXn94qfXL555Jw+fnzz8
9eTRo5OHv2SGLK09lISm1qsfvvznyWfe38++f/X4azdemPg/fv7899++cgNhposQvPzm6Z/Pn778
9ou/fnzsgO9wNDbhIxJj4d3Ax95tFsPEdAhsz/GYv5nGKELE1NhJQoESpEZx2O/LyELfmCOKHLhd
bEfwLgeKcQGvze5bDg8jPpPEYfF6FFvAA8boLuPOKFxXYxlhHs2S0D04n5m42wgducbuosTKb3+W
ArcSl8luhC03b1GUSBTiBEtPPWOHGDtmd48QK64HZMKZYFPp3SPeLiLOkIzI2KqmhdIeiSEvc5eD
kG8rNgd3vV1GXbPu4SMbCW8Fog7nR5haYbyGZhLFLpMjFFMz4PtIRi4nh3M+MXF9ISHTIabM6wdY
CJfOTQ7zNZJ+HejFnfYDOo9tJJfk0GVzHzFmInvssBuhOHVhhySJTOzH4hBKFHm3mHTBD5j9hqh7
yANK1qb7LsFWuk9ngzvArKZLiwJRT2bckctrmFn1O5zTKcKaaoD4LT6PSXIquS/RevP/pXUg0pff
PXHM6qIS+g4nzjdqb4nG1+GWybvLeEAuPnf30Cy5heF1WW1g76n7PXX77zx1r3uf3z5hLzga6Fst
FbOlul64x2vX7VNC6VDOKd4XeukuoDMFAxAqPb0/xeU+Lo3gUr3JMICFCznSOh5n8lMio2GEUljf
V31lJBS56VB4KROw7Ndip22Fp7P4gAXZdrVaVVvTjDwEkgt5pVnKYashM3SrvdiClea1t6HeKhcO
KN03ccIYzHai7nCiXQhVkPTGHILmcELP7K14senw4rIyX6RqxQtwrcwKLJ08WHB1/GYDVEAJdlSI
4kDlKUt1kV2dzLeZ6XXBtCoA1hFFBSwyval8XTs9Nbus1M6QacsJo9xsJ3RkdA8TEQpwXp1KehY3
3jTXm4uUWu6pUOjxoLQWbrQv/5cX58016C1zA01MpqCJd9zxW/UmlMwEpR1/Ctt+uIxTqB2hlryI
hnBgNpE8e+HPwywpF7KHRJQFXJNOxgYxkZh7lMQdX02/TANNNIdo36o1IIQL69wm0MpFcw6SbicZ
T6d4Is20GxIV6ewWGD7jCudTrX5+sNJkM0j3MAqOvTGd8dsISqzZrqoABkTA6U81i2ZA4DizJLJF
/S01ppx2zfNEXUOZHNE0QnlHMck8g2sqL93Rd2UMjLt8zhBQIyR5IxyHqsGaQbW6adk1Mh/Wdt3T
lVTkDNJc9EyLVVTXdLOYNULRBpZieb4mb3hVhBg4zezwGXUvU+5mwXVL64SyS0DAy/g5uu4ZGoLh
2mIwyzXl8SoNK87OpXbvKCZ4imtnaRIG67cKs0txK3uEczgQnqvzg95y1YJoWqwrdaRdnyYOUOqN
w2rHh88DcD7xAK7gA4MPspqS1ZQMruCrAbSL7Ki/4+cXhQSeZ5ISUy8k9QLTKCSNQtIsJM1C0iok
Ld/TZ+LwHUYdh/teceQNPSw/Is/XFvb3m+1/AQAA//8DAFBLAwQUAAYACAAAACEAoggHkfwEAACH
JgAAGwAAAGNsaXBib2FyZC9jaGFydHMvc3R5bGUxLnhtbOxa4W7iOBB+lcgP0AA9WlqVSt1WK51E
b6u9le63SRzwrmPnbLOUPv2NncTECWnYpbCFvX94Ejmeb2a+Gc9wE6nraI6l/luvGAmeU8ZBoMZo
rnV2HYYqmpMUq7OURlIokeizSKShSBIakTCWeEn5LBz0+oNwvQsqtsGNXURGOHwiETLFWp0JOSv3
SBns0rsIU0w5Cmg8RoPBJbq9gePhZ6q+UM2IXTH+mSTwwvMY9VBoRQllrCEkSUIi3RAngq+FKeVC
wkfwtVWT3DMZfMdsjPRz34rZIn0UcS67GPZ69ov4GsSfkiQXn5fisLLL7U0IBy++Zc8Yk+TzkwzU
yxj1zT7BNyI5/AaljRbmdV/PCGsyE3J1B9ofs+Iqe5IGYcaD5RhdDQdDFEQ4G6OEYQ0/0wxsrfgM
BZjNAJFIFxYRjMYfwbJbmqdf2sE3z6gU18wDxqp+QIoFj40dzEFzc+QHB7tUTHe12XINW9lYuJME
B6mIIZgwY2L5lzDqfPpOpKQxAXWtbEI5KWW5vx/Mwwu/q7ppaS0PnFpwTGd967I1CE/CwG2x6Qya
BzPWeIKnBHwT/ONg9rJhVGGky9K3fZcflOKay7czUotbx56abnVvPHehD6d7/O2t2XgbN2e6xc0t
IVXJoxYfm47rbOKbylmwZiovtH6ck8DSUxGvIN9IoU2eDFQWfaRS6QlW+glLyMx9FAATaUM9CRAR
EC+jGQrmQr7UZeY9SOXwBAVLabhb/bvAkqCA/cmB3c4vhpcXKNB20R8NRiMUyOqTafUJ5hFslRN9
kC/uNaxzG6vsbqGBKHVBTrkeOSNv9EAjfBKUb+eP5iOAjjLFjsv2GL5YJuGilrBv7VxC/CTBZnM4
WpNiTY2QO6755evtVucPW8XlCeJQaO6QMIm1iUWH+S2X23e8otIyAYC+T4/I8+dgNLwsKyTJY1uF
vpaMW3wFX7dWNJ7zOJAcbI9YQmn6dsB1AP6O482Ugq5m7SL9Fjs0uNtDv4J1Df8JXkGKDdQqnQq4
j0RURgz4VtEXMkZDww1VCviHSpJInB6hv1cuBAdwdx8og/kXPD3ui2VZynB7uwDH8Nz25K5avs1i
seQf8Aa2ygPE4/CyW7ATh5va7iez+qay8Ad7Cl5Z6Nm5i57eoKHhfXwzr1WsEUuRueRysBvSK6ap
cPlegsL1gfwK31n4tQq/K1tXsYTugZAH9XnjO78QWIegD6zDewdgPSzhFiQOyCQdqHp0bqLbCTaG
3vrwM2guMSh9H/HXd6TOvqNvT90/U601AXUS0z5uVsr7yj0dHrNviMuukh+GV6V4hzBs4jmnE/Fb
ZQ/XCPLRdW2jHdD1sSQ4JvK3gtYlCh9al1Z2gJbV0CQzwuPDEoKt9ys9YaeWr60DoaYt8Fu1/d89
6mBrHTMm9NHNOAyjlwe3lioX5w9HNaup6lF0vhSRlKhjnxd2u2BNz3z5P6WNkYv9WpB7N7eOu0YN
TX1Kc/c/zPR2auchlem7mY1Y2RQrYirn4o8FJsLW6msJ1G6eNundStonCi0t5TdpR2xVEvavekNQ
3A7dd+qxZTBAesBqnv/xQa3Ug9DFmMIfmlvoPMAcfO9gbOrCZE8pcoOui+ygN/YdulStU8+tO+HG
J+tVyS7XMyCzpnetAYX/6yzI6Sc9X80ljOCbRPSLLp6uM5EbqqtVkZ/dUMT6H2O3/wEAAP//AwBQ
SwMEFAAGAAgAAAAhALj2+Hq8EgAASnsAABsAAABjbGlwYm9hcmQvY2hhcnRzL2NoYXJ0MS54bWzs
nVtz20YShd+3av8Dw83bli0AvKsib8mylcvaiWM72ap9g0hI4hoEGACypfz67QEwpylZp4eJb1Vb
6weXCDZ7er7pmcEcDjHf/ON6kw/eZlW9LoujYfwwGg6yYlmu1sXF0fCX16cP5sNB3aTFKs3LIjsa
3mT18B+P/vqXb5aHy8u0al5t02U2ECdFfbg8Gl42zfbw4KBeXmabtH5YbrNC3jsvq03ayMvq4mBV
pe/E+SY/SKJoetA6GfYO0j/hYJOuC//5ap/Pl+fn62X2pFxebbKi6aKosjxthEB9ud7Ww0dSuVXa
ZPEiGg/epvnRMBoeuIt5Wlx0F7LiwS+vuotVeVWsstVJWRWCccd+szw8zpusKsTVSVk0Ulpfz81e
pDZp9eZq+2BZbrYS3Nk6Xzc3bbgSoPg+uSylHoOX2W9X6yqrj4bLeOxByJ/vodisl1VZl+fNQ/F4
0FHwreHczg7mB0nfHlLZeHxYNzd51lUojhJX2wOU24Zwmub5Wbp849jsGMNU33cfvAvDfaptfvdH
etWUr9dNnj3J8qzJVn2xHeJtXjbHVZY6wzy9Ka8a99cmLa7S/Bled++8TquLrOk+vi6kSToX18/L
VV+XbHWRdRdv7rt43X12+jCORrNoOh9PovF0Ohs/fdDWa3l40xkkD6ejJJ7Jv1EyT+bz6QQW7zqL
6OEiSUaR/DdZxPFkNp92xV76t+fRfJzEk/k0ns7no/F07N4/uFszuaCVztdFduI6niNwIbm3lc7U
+eu6abXqCnmbVjcnZV7eSklppqxyn1yv+mr2mV1Wq6zqw+o+31w7u7qpXmbn7q/zR68us6yJv/r6
8dexC7K9Ku+fpNLZncW2OZGu0KOPOy/bZiBFuQ7kLN4++rEsBo8lkZv0TFLrX+vmUhpz8PQ3acja
OX3b1n8rtZMX6rx70UYif/ahbV9IVdLDvBi8Oxom88lsMhws0+3RsCpWUlx6WJf5enW6zvP2RXVx
dpL3dTw9jeRfS/uOWdubXSs4x+5z2fl5tmye1U3XNHVbqmui6k2Hsr7ZnJUyhrpxopAxsvW6ayDv
uMoXV5t7UCaHjuc4AlExA9Fu3DyRzH30bSapnOatWTuatlffoz5a3M89Tua38brm6pom7ptGMpSZ
JN4koSYjmLRV0XbUgsbeJKYmE28SzVgsU5hQLzNvwms0h8mIFbSACS0o9mkdx1PmJla+tE4xAI8o
4BiER233u4+wTABdN1vQWsVAPFrQkMGYA4wVMq+WUuYhA3NC/SQe84LSSUA5oa2VgHKSsKonSpmH
4ynHvLUSYOaZnHjMC5o8CShH3AaUR9xGKdNWH3nK8YgiHAEzT8KRYqYIR8Cc0JhHHvOCttYIlEfc
xlOOjaKAOaGJOgJmw0Yxj1mGjYE5pnjGwMwH3LFipp19DMwxbfaxx2w0+xicDT/KmccDziPORzlz
P+Ac02ltAs683SfKmbb7RDnzssA5oTFPwNmwAeeE8pkoZzoaTpQzr5dypn1nAs58cJ4qZ5pjU3Dm
E8pUOdN6TZUz7TtyE93NgUY+T5UzHeqm4Dyi7T4FZ95eU+XM/YDziObPDJx535mBM49ntgfn2R6c
Z8qZ5pjckfdtkdDcmIGzEfMenGfKmebGTDnTmOfgzPvpXDnT9pqDc0z71xyc+V3CHJxjHjM48/FZ
Fp99Wxh+wJnfz86VM48HnHk8C3Dmy4EFOHOGC+VMx42FcqZ9cAHOEbdRzrTuC+XM/YBzRMf5BTjz
W78FOEc0D2NZ5/qGpx0jjpS04UlR06qJaILiaN3iCLBjwwi0ee+II+CO6BATR8rbQKDAjZhAnI9W
sS4LeSrFui40AteFIU+CWFeGlhGIRzR1RQHwbcfzUpa6MOIwd5aHdPSLYxDnQ1Icg7iRBVghxgZx
XSNGdKSIdZFodCldJUa8dokS55m5s06kdyKycAFx3jl3loq8WRIQtzgpcc4Jq0W+Jo+xWlzwiHS1
yKeBWJeLRsthvShDEFt8yYrdo7SK24f3SMcUPlzoovFu1xQZU5W+7oWXN3u9sN6UZeOl4l5U7NXb
uyJuL7buiLj9FS7i/vABIq4KuM/X181V9ZmFXPfdUgYpN10u5YuV7iuHL6rl/vC1aLmC9ZNruQs+
gPfJvaBjiZ/B53RI8tP3nE7xflzjFr6T8VJ8D+M+fPfiPvxYxi38QMYtMFUbJv7WiMeKadowCXOF
emt4AVnaOpieDS+eLVfWMDXP+dANujQWTMtGQoIuzUhMybxGmJC58I3p2IgFdHksPnENL6BL+ymm
4QVH5+ny3q5TML0JwQzM0xsTME8GTL+GFz8oLOhkiKmXN+MIdHmNwnQx6Ro1CtOFTsvDhUxrmPgB
lycmRFre0tBoDRPkLk2pMejS6WEcpjsOD7tj0KU3YlBn+XcfEGd5M0Kb5Q0wCY+7UGZ5n554ukYs
ni7vJJBljYI8XZ4vEGV5V4Mmy7lAkuXhQpHllYYgyxMTeqxREOjSERNqrOEFuUvTG1qsES5yl3qB
EsubEUIsDxc6LG9pyLC8AaDCGgWBLq+Rz10jFtClAzwUWJ51EGB5uNBfOV3Ir7wZob4aXvzIYJj4
WY3XaB6mC+WVN+Pc0zW4YGSgzThH7tJxF6orHzwguvJKQ3M1TECXx+Lp8mZcgC4dGaC3GrF4uryl
obYasXi6vI1Ua+UdSaVWyw9uG2hjq9DKQ1adldc8jsDYKMsPEByyiqxWWUhieoMXR3twxrLNKAsC
q9EWWLjx7qDyqtFeWLpZ8YAzzWXVVi0/4WxWZdVoLwirRlm6gOPtBVnV4IMlnFVWeMSIsYizygJn
ns9QVI2+A0HVihn5bJSFfKZ35vJVdVC52VFTeVtgMWfwwWrOqBe0VKPvYD1n9B0s6Iw8hJBq2YDz
ndnko8qo/f7eHRm1v8Jl1J8/QEZ9fy/s59kIe59+2urFsuf11pbZz7sX9mennwrPT66fxndySHeo
ei2KW/h5mVv4UYx/selve7iFH8F4KX4+5j78LMF9+D7FffiRi1tgIjZMQJWuHDAN82D1S07eemGw
+gUnjyWMVr/c5LEALjcJ08X0y+li9uXoMPkaXsJZi6nXKCict5h4jViQubSNoJ8aXkCXNgAmXe4F
+qlhgiGBhosZl6PDhMu/Ccd8a8QSzl3MtoYX5C6vUZgu9FNeEPRTwwR0aTNCPzW8YGTgXpC7tNLQ
T3kzQj81YgFdHgvo0lign/KCoJ/ycKGfGibhkQH6qREL6NJK675WboKRgZuE6UI/5eFCPzVMwrMa
9FNOF/qpYRLOXd3OSvMF+qlRUJgu9FPDC3KXthH0U04X+ikvCPqpYYLc5bGALkWnm1i5F4y73AR0
6eof+qnBBXRpuNBPuRfop4ZJeNzVzau00rp3lZuERwbop7yloZ/y6VP3rfJYQJeaQD/lsUA/NUzC
dHXLKo8FuctNwnShnxrhIndp1kE/5Smlm1VpuKqfcjeqn/KA3a+V+z2vNGLVT62ywohVP7XiwQBh
xBOmLLsbfb0MhshiXtZey7bwDBfrws0oKzwKx9BPjbbYZ+2GzS9GW+jqje9uxPYXK549OOsCjrfX
Hiu4nV2pnDP2wBh132MRF+uWVCNm5LNhg3w2bJDPRr3AmfvRpZxhs0c+Qz812l1XczxmXc4Z8WBs
NvzswVlXdEZZjPNH1U9H3W/Pd/TT/grVT//28unpV26z7p96mAA+rb+FFlcf96kB94ml3WMavqRY
ipq33HQjsYDUhwPs+cgA8sCA21uoP2qetPyWhzt50l+hefL18fEHCO26X/mzPnDi/tRpH1lxtZEn
j3SPjJj6J1HIAyeuNj+dn3eXx/6yE+W9p/axFF9QopdWEI3etcWnF+n5l1z+Foha4OaPWuCWhFrg
xo9aYPimwy4Gb+oDUyS1wMBNLTA9Ugu92+MmmBy5SRirivTcC8BSbHqjx72E0eptHvcShosvyfkP
EPUejxakt3jcJExXb/C4F9DlJshbbhKmiy/HDS5hukk4d/XOjoarIj03CdPV2zruJUxXRXruJUxX
b+m4lzBdeRaQ//X17clcv/BUkZ4WpCI9NwnTVZGeewnT1edQcC9huirScy9hutjkzHuAivS0IBXp
uUmYLjY5G7GALh13scnZ8BKmi03OhpcwXRXpKRcV6WmNsMmZx6IiPS8ofLeATc5GQeFx9w+J9Dzc
MN1peGRQkZ4WpCI9NwnnLjY5c3TY5GyYhOnuPGiCDYez8P2YivS80mG6KtLT3FWRnhaETc6ci4r0
3AtGBm4SpqsiPfcSpqsiPfcCuvTJUSrSUy8q0nOTcO6qSM+9hOlikzNvRhXpeUFhuirScy+gS012
RHrDZo/1hIr0hp/w2KubnDm/HZHeKAuTG+2UusnZKis8AOsmZ8PPXsu2PTirSM/rvs/KTUV6w094
pNBNzlbdw9msm5wtP3vk8x4LuHiPFdyOSM/57LGG003ORr1UpDfKQj4bNntw1t1Whp89OO+xlNNN
zkbdVaTn8eyxmotVpDf87JHP2ORsxbwHZ/xu9a6fjyq+Tt4T6fsrhvj63UcTXz/PDufdR/1G0eNI
FmLdI32/pHb6XaudCspPrp3SGyM/WdAvPv0tD/3Rv5+OqYG/3aEGvkNRAz9qUQPflWg1/fxLPfjB
inrA1Etd4NdF3CIIE5Mu9xHECbGU+wgChVTKfQSRQiilPjDNcgufn9wiyBQTLPcRZIrJlfsIMoVA
yn0EmUIepT4wpXILz5TmOqZT7iPY6zGV8lKC/R7TKI/D93xuEWQKUZT6gCbKLTxT+oMuPJyX+wgy
xUMfuI8gU2xZ5j6CTPHAB+7Dj6fUAkoot/BMuUWw70MG5T6CfR8iKPfh+z7NdUig3IfPU5pBEECp
D+if3CLIFOon9xHMU2if3EcwT6F8UqZ41C638Ex5HME8hepJfUD05BbBPIXkSesCxZOXEmQKvZP7
CPZ9qJ3cR5AptE7qA1IntwgyhdDJfQT7Pp7lwH34vs8tgkyhcXIfQaZQOKkPCJzcIsgU8ib34ZnS
TIa4yX0EmS6CTKFs8lI8Uxqp6prUie49NkyCWHXnseHFgzVMgiOAPhzX8BJkq5uODS9huvssqYLz
lW445rHssaqCksmTYY91FTYbG7GE6WKrseHF0+UmWFzxGkHCNLyEcze8vlL90ijI564RbnBYiMNr
LDn2qN8DYcQSphteZqlwyQvCQotXGrKlYRIeGbAJxYjF0zUKCucuFMs7Xj5IsFw9O8trt3Glvizf
PcsusmL1z6w/c60/rcy982van7m1c+0kbX5MN/0xbzvXX2XVvddfZJV7eOytc8+c78dXZ3I82av1
77uupFIa2t1n88oBdtff9zs6HyTRYjyfjMbyk0aRHe95K57LRvzuPLFb57kt0+a4PXbNcFYvU/nM
hfNbVmuJvj24sKvCZl08T697zzuGq/ZMvc6mfzJwev2i7A8rPOui3KT/KavX6+Wb53K0WWfrjzOT
08zWBX+zkQ9Jo8FhkV03r8vOqztzsD7uD5vryfjqy/F/7t2sj8MdAfjvrOo/6F7dijg/y4/zi6K7
tmyqzr9clT2ztT/xz03iLfOifH6VN+tnb3OJrPtM+460IijLg5YJbo1wh+Kfwt2Hs4M738H9bbVe
uQzo8v3WgXaLichQ3Xl253I4pfy52a6OhnVxMRykwkHOexQG7x9z5/cKd3Vurt2c2m4txo7jeNKf
fXd7x/HcX/4YO44Fc5tPtyoo+9RPN81At6gfDfs96nLGaHklffHZuniTSS37JP00KVn3nIvSnQ0o
2eKP+sOF0OF/zXV39OBZubp5UQ2qsjkaPmg3cjuyg3q7PF1XdfMsrZsXaSWnmsZDd8Zq85P8d56X
clxhludy0ui67q7LWZ1l9ftw8K5yxxfWchpilUkrF0u53LVz/+KkkdctHIm5bl65AzrbCmxdLfp6
rbLzlxJV/fvRcOHCOXPHLw7W7f9X/RmFcqZrtX4j57kW5av2r+FAjjN0J8DKSaDykbTOXF52Bzfe
+aUDtqPzFJv6XLqdYiN/2U4xdxZrMWhuttm5HC57NPz7pniQN67byHGM6Z03srR7Y1nfeWNZuzek
pI5H+2dPSKYU1y6OkjvSVZrDH+cq5oJSsrdr4n6EujN+6chujF/tW4+z5l2W9WPWWfeijUpOuuwG
Hynq1umm7XznBve8/Qtjaj9Id1efFk1144xwjGf/Q5v3Bnpxf+cjpeSgHCe6MyTKTPtJesT/e4Kc
CPq/0hOQSC7tXMb+uq5/KvI+j/rxerWut4+lR72pj/s5/SLdunzvbuaeyFRcuzFQblJ28k9c4xjg
2yNr2+MD41zXx/94x5Z7FHc0c/4kbdJBJT1Jzo39ftWpne7e45etOwL6dpi7n2lHiTbu9gjsR/8V
AAAA//8DAFBLAwQUAAYACAAAACEAHBSnqAIBAABuAwAAHAAAAGNsaXBib2FyZC9jaGFydHMvY29s
b3JzMS54bWyck0FugzAQRa+CfAAMJKEVCtlkXXXRE4wGO1iyPZHtps3ta0ihhapI4N3M139/ZiQf
0VdImtxbuGuRfBptY8PXrA3hWnHusRUGfGoUOvIkQ4pkOEmpUPDGwYeyF15kecGxBRd6CvvGwB8K
XYWNEZKcgeBTcpeBYXSkZCU3oCxLjAhtzfCOWrBENTXLM3Y6QtVPI87aJTfQNQNEYUPO+L9asaDt
FrT9gnZY0MpOiye9gVMQFNlZ2Q2q380LNY8Nyiy+zsN/m6aIued58PSoVykfqHi+7agV8ePI0/j9
hvjDBs/T4JnG74b2ik1G1ArPOPI0fmzPUF35871OXwAAAP//AwBQSwECLQAUAAYACAAAACEADzw7
JzgBAAB7AwAAEwAAAAAAAAAAAAAAAAAAAAAAW0NvbnRlbnRfVHlwZXNdLnhtbFBLAQItABQABgAI
AAAAIQCtMD/xwQAAADIBAAALAAAAAAAAAAAAAAAAAGkBAABfcmVscy8ucmVsc1BLAQItABQABgAI
AAAAIQAvHR1XeAEAABkEAAAfAAAAAAAAAAAAAAAAAFMCAABjbGlwYm9hcmQvZHJhd2luZ3MvZHJh
d2luZzEueG1sUEsBAi0AFAAGAAgAAAAhAMfrCjpXAQAAngIAACYAAAAAAAAAAAAAAAAACAQAAGNs
aXBib2FyZC9jaGFydHMvX3JlbHMvY2hhcnQxLnhtbC5yZWxzUEsBAi0AFAAGAAgAAAAhAGcD7obO
AAAArAEAACoAAAAAAAAAAAAAAAAAowUAAGNsaXBib2FyZC9kcmF3aW5ncy9fcmVscy9kcmF3aW5n
MS54bWwucmVsc1BLAQItABQABgAIAAAAIQC2OwQiVAYAAAsaAAAaAAAAAAAAAAAAAAAAALkGAABj
bGlwYm9hcmQvdGhlbWUvdGhlbWUxLnhtbFBLAQItABQABgAIAAAAIQCiCAeR/AQAAIcmAAAbAAAA
AAAAAAAAAAAAAEUNAABjbGlwYm9hcmQvY2hhcnRzL3N0eWxlMS54bWxQSwECLQAUAAYACAAAACEA
uPb4erwSAABKewAAGwAAAAAAAAAAAAAAAAB6EgAAY2xpcGJvYXJkL2NoYXJ0cy9jaGFydDEueG1s
UEsBAi0AFAAGAAgAAAAhABwUp6gCAQAAbgMAABwAAAAAAAAAAAAAAAAAbyUAAGNsaXBib2FyZC9j
aGFydHMvY29sb3JzMS54bWxQSwUGAAAAAAkACQCXAgAAqyYAAAAA
" o:spid="_x0000_i1025" style="height: 318.75pt; visibility: visible; width: 452.25pt;" type="#_x0000_t75">
<v:imagedata o:title="" src="file:///C:\Users\JASON_~1\AppData\Local\Temp\msohtmlclip1\01\clip_image001.png">
<o:lock aspectratio="f" v:ext="edit">
</o:lock></v:imagedata></v:shape><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>Use of Class Versus
Struct To Store Data</b></h3>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Commonly known facts:<o:p></o:p></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Class instances get allocate on the heap<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Class instances get via a copied pointer
reference (size is 4/8 bytes for 32/64 bit process)<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Class instances have additional overhead of 8/16 bytes for 32/64 bit process (in fact
.NET aligns objects to the minimum of 12/24 bytes object size for 32/64 bit
processes)<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Structs have no additional overhead, the amount
of memory is the sum of all fields in the struct<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Structs declared in a class exist on the heap
(not the stack!)<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast" style="mso-list: l1 level1 lfo1; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Structs, by default, when passed to a method are
copies of the original struct (a byte-by-byte copy), so any changes made to the
struct from inside the method are local to that method<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
With this in mind, it appears that the original application developer
used a struct to store minute-by-minute stock market pricing data, rather than
a class. I can see their thinking: in a
32 bit process the amount of memory required to store an array of 10’s of
thousands structs would have been about half that required if a class were used
(there is even more of difference in a 64 bit process)<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In addition to using structs to store the market data info,
various supporting calculations required value equality comparison and were stored
in Dictionary based collections, hence a large of calls to Equals and
GetHashCode:<o:p></o:p></div>
<div class="MsoNormal" style="margin-left: 36.0pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin: 0cm 0cm 0.0001pt 36pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> GetHashCode()<o:p></o:p></span></div>
<pre style="background: white; margin-left: 36.0pt;"><span style="color: blue; font-family: Consolas;">public</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">override</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">bool</span><span style="font-family: Consolas;"> Equals(</span><span style="color: blue; font-family: Consolas;">object</span><span style="font-family: Consolas;"> obj)<o:p></o:p></span></pre>
<pre style="background: white; margin-left: 36.0pt;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<div class="MsoNormal">
Unfortunately, these structs did not to override the
GetHashCode and Equals methods, therefore using .NET’s default values, which resulted
in some rather surprising performance issues.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>Blittable vs Non-Blittable
Types</b></h3>
<div class="MsoNormal">
Performance can be further decreased by the type of fields
that the default implementation has to reflect over. <i>Non-blittable</i>
types, are those which do not have the same structure in memory in managed and
unmanaged code, and as such cannot be shared directly – reducing performance of
the default method implementations.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Commonly used blittable type (there are many more):</div>
<div class="MsoNormal">
</div>
<ul>
<li>System.Byte</li>
<li>System.SByte</li>
<li>System.Int16</li>
<li>System.UInt16</li>
<li>System.Int32</li>
<li>System.UInt32</li>
<li>System.Int64</li>
<li>System.IntPtr</li>
<li>System.UIntPtr</li>
</ul>
<br />
<div>
Commonly used non-blittable types:</div>
<div class="MsoNormal" style="margin-left: 18.0pt;">
</div>
<ul>
<li>System.Boolean</li>
<li>System.Char</li>
<li>System.Object</li>
<li>System.String</li>
</ul>
<o:p></o:p><br />
<div class="MsoNormal" style="margin-left: 18.0pt;">
<o:p></o:p></div>
<div class="MsoNormal" style="margin-left: 18.0pt;">
<o:p></o:p></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal" style="margin-left: 18.0pt;">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
For further info, see <a href="http://en.wikipedia.org/wiki/Blittable_types">http://en.wikipedia.org/wiki/Blittable_types</a>
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>Test Code Generate Results</b></h3>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’m going to walk through a selection of code snippets to
show how much of a performance difference you can see by implementing Equals
and GetHashCode.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
As part of my profiling samples, I’ve created a base class (<i>TimedActionBase</i>) that, when used in
conjunction with NUnit and Resharper, makes it really easy to run snippets of
test code with a single mouse click and provide a statistical average for the
area under test. You’ll find a link to
the code at the bottom of this article. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
You won’t need Resharper to compile the test code, but
you’ll need to run <b><i>Install-Package NUnit</i></b> using
the Package Manager Console to download and install NUnit from NuGet.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">I’ll just summarise the base test class here, as it’s the actual tests that are more important. <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"> </span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"> </span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">Each test class derives from TimedActionBase and implements the <i>TimedAction()</i> method. This method gets called once initially (unloading any timed JIT overhead) and then is called a set number of times, with a percentile calculation used to provide a meaningful idea of how long the method takes to execute. I can then run <b>SinglePassRunner()</b> if a want a single timed pass (this calls the test method 100 times), or, in order to build up the data for the graphs <b>MultiPassRunner()</b>: <o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"> </span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"> </span></pre>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System.Collections.Generic;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> System.Diagnostics;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">using</span><span style="font-family: Consolas; font-size: 10pt;"> NUnit.Framework;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">[</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TestFixture</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">abstract</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">> _durations = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;">>();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Stopwatch</span><span style="font-family: Consolas; font-size: 10pt;"> _timer = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Stopwatch</span><span style="font-family: Consolas; font-size: 10pt;">();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span><span style="font-family: Consolas; font-size: 10pt;"> _headerLogged;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">abstract</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Test</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> SinglePassRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> [</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Test</span><span style="font-family: Consolas; font-size: 10pt;">]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> MultiPassRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> NoOfPasses = 180;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">for</span><span style="font-family: Consolas; font-size: 10pt;"> (</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> i = 0; i < NoOfPasses; i ++)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedActionRunner()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> DefaultTestCycles = 100;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: green; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">// Prerun TimedAction method in case there's any JIT overhead</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _durations.Clear();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">for</span><span style="font-family: Consolas; font-size: 10pt;"> (</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> cycle = 1; cycle <= DefaultTestCycles; cycle++)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Start();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> TimedAction();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Stop();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> duration = _timer.Elapsed;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _durations.Add(duration);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _timer.Reset();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> CalculateStatistics();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> CalculateStatistics()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> average = _durations.AverageTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> min = _durations.MinTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> max = _durations.MaxTotalMilliseconds();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> deviation = _durations.Deviation();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">const</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">double</span><span style="font-family: Consolas; font-size: 10pt;"> Percentile = 95D;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> percentile = _durations.Percentile(Percentile);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> header = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Empty;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">if</span><span style="font-family: Consolas; font-size: 10pt;"> (!_headerLogged)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> _headerLogged = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">true</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> header = </span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"Timings(ms) \tPercentile\tAverage\tMinimum\tMaxmimum\tDeviation\n"</span><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Log(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Concat(header, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;">.Join(</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"\t"</span><span style="font-family: Consolas; font-size: 10pt;">, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;">[]<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> GetType().Name,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(percentile), FormatTimeSpan(average),<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(min), FormatTimeSpan(max),<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(deviation)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })));<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">static</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> FormatTimeSpan(</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimeSpan</span><span style="font-family: Consolas; font-size: 10pt;"> span)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span><span style="font-family: Consolas; font-size: 10pt;"> span.TotalMilliseconds.ToString(</span><span style="color: #a31515; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">"N0"</span><span style="font-family: Consolas; font-size: 10pt;">);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">static</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> Log(</span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">string</span><span style="font-family: Consolas; font-size: 10pt;"> format, </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">params</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span><span style="font-family: Consolas; font-size: 10pt;">[] args)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Console</span><span style="font-family: Consolas; font-size: 10pt;">.WriteLine(format, args);<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
To compare the performance of different implementations, all
of the sample tests below follow the same basic steps:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l2 level1 lfo4; text-indent: -18.0pt;">
<!--[if !supportLists]-->1.<span style="font-size: 7pt; font-stretch: normal;">
</span><!--[endif]-->Define a struct with slightly different
implementation under test (eg, with or without Equals)<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l2 level1 lfo4; text-indent: -18.0pt;">
<!--[if !supportLists]-->2.<span style="font-size: 7pt; font-stretch: normal;">
</span><!--[endif]-->Create a List containing 50,000 structs<o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l2 level1 lfo4; text-indent: -18.0pt;">
<!--[if !supportLists]-->3.<span style="font-size: 7pt; font-stretch: normal;">
</span><!--[endif]-->Create a single comparison struct<o:p></o:p></div>
<div class="MsoListParagraphCxSpLast" style="mso-list: l2 level1 lfo4; text-indent: -18.0pt;">
<!--[if !supportLists]-->4.<span style="font-size: 7pt; font-stretch: normal;">
</span><!--[endif]-->Walk through the list using the .Equals method and
calculate how long that timed operation took<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>1. Not Overriding Equals()
With Non Blittable Fields (Slowest Speed)</b></h3>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Equals Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
No<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
117<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">SlowNonBlittableWithoutEqualsTest</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">struct</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field2;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field3</span></b><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">> _items = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Enumerable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">EqualsTestParams</span><span style="font-family: Consolas; font-size: 10pt;">.NumberOfItems)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(n => </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = n + 1.1M,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = n + 2.2M,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = n + 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> _comparison = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = 1.1M, Field2 = 2.2M, Field3 = 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> };<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> allEqual = _items<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(item => item.Equals(_comparison))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>2. Not Overriding
Equals() With Mixture of Blittable/Non Blittable Fields (Slow)</b></h3>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Equals Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
No<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
2<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
1<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
90<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">SlowBlittableMixtureWithoutEqualsTest</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">struct</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field2;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field3;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">> _items = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Enumerable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">EqualsTestParams</span><span style="font-family: Consolas; font-size: 10pt;">.NumberOfItems)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(n => </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = n + 1.1M,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = n + 2,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = n + 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> _comparison = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = 1.1M, Field2 = 2, Field3 = 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> };<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> allEqual = _items<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(item => item.Equals(_comparison))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>3. Not Overriding
Equals() With All Blittable Fields (Medium)</b></h3>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Equals Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
No<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
13<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MediumBlittableWithoutEqualsTest</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">struct</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> Field1;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> Field2;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span><span style="font-family: Consolas; font-size: 10pt;"> Field3;<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">> _items = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Enumerable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">EqualsTestParams</span><span style="font-family: Consolas; font-size: 10pt;">.NumberOfItems)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(n => </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = n + 1,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = n + 2,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = n + 3<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> _comparison = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = 1, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = 2, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = 3<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> };<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> allEqual = _items<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(item => item.Equals(_comparison))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
<br /></div>
<h3>
<b>4. Overriding
Equals() With Non-Blittable Fields (Medium)</b></h3>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Equals Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
Yes<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
12<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MediumNonBlittableWithEqualsTest</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">struct</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IEquatable</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field2;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Decimal</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field3;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals(</span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> obj)<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">if</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> (obj == </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">null</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> || (!(obj </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">is</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;">)))<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">false</span></b><b><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals((</span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;">)obj);<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals(</span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> other)<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1 == other.Field1 &&<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> Field2 == other.Field2 &&<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> Field3 == other.Field3;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">> _items = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Enumerable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">EqualsTestParams</span><span style="font-family: Consolas; font-size: 10pt;">.NumberOfItems)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(n => </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = n + 1.1M, Field2 = n + 2.2M, Field3 = n + 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> _comparison = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = 1.1M, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = 2.2M, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = 3.3M<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> };<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> allEqual = _items<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(item => item.Equals(_comparison))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
<b>5. Overriding
Equals() With Blittable Fields (Fastest)<o:p></o:p></b></div>
<table border="1" cellpadding="0" cellspacing="0" class="MsoTableGrid" style="border-collapse: collapse; border: none; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-yfti-tbllook: 1184;">
<tbody>
<tr>
<td style="border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Equals Overridden<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Non-Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Blittable Fields<o:p></o:p></b></div>
</td>
<td style="border-left: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<b>Typical Milliseconds<o:p></o:p></b></div>
</td>
</tr>
<tr>
<td style="border-top: none; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 100.45pt;" valign="top" width="134"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
Yes<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 106.25pt;" valign="top" width="142"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
0<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 83.85pt;" valign="top" width="112"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
3<o:p></o:p></div>
</td>
<td style="border-bottom: solid windowtext 1.0pt; border-left: none; border-right: solid windowtext 1.0pt; border-top: none; mso-border-alt: solid windowtext .5pt; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt; width: 107.85pt;" valign="top" width="144"><div align="center" class="MsoNormal" style="margin-bottom: 0.0001pt; text-align: center;">
6<o:p></o:p></div>
</td>
</tr>
</tbody></table>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">class</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">FastBlittableWithEqualsTest</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">TimedActionBase</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">{<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">struct</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> : </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">IEquatable</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field2;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">int</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field3;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals(</span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">object</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> obj)<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">if</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> (obj == </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">null</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> || (!(obj </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">is</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;">)))<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">false</span></b><b><span style="font-family: Consolas; font-size: 10pt;">;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals((</span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;">)obj);<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">public</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">bool</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Equals(</span></b><b><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> other)<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> </span></b><b><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">return</span></b><b><span style="font-family: Consolas; font-size: 10pt;"> Field1 == other.Field1 &&<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> Field2 == other.Field2 &&<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> Field3 == other.Field3;<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<b><span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></b></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">List</span><span style="font-family: Consolas; font-size: 10pt;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;">> _items = </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">Enumerable</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Range(1, </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">EqualsTestParams</span><span style="font-family: Consolas; font-size: 10pt;">.NumberOfItems)<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(n => </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> { <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = n+1,<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = n+2, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = n+3 <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> })<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">private</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">readonly</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"> _comparison = </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">new</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">MyStruct</span><span style="font-family: Consolas; font-size: 10pt;"><o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field1 = 1, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field2 = 2, <o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> Field3 = 3<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> };<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">protected</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">override</span><span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">void</span><span style="font-family: Consolas; font-size: 10pt;"> TimedAction()<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> {<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> </span><span style="color: blue; font-family: Consolas; font-size: 10.0pt; mso-fareast-font-family: "Times New Roman"; mso-fareast-language: EN-GB;">var</span><span style="font-family: Consolas; font-size: 10pt;"> allEqual = _items<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .Select(item => item.Equals(_comparison))<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> .ToList();<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;"> }<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: Consolas; font-size: 10pt;">}<o:p></o:p></span></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
The numbers are very surprising.<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
You can view similar results for the GetHashCode performance test
post.<o:p></o:p><br />
<br />
<br /></div>
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2116997&authkey=AHi17YC2OFp6XLc" width="98"></iframe>Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-84845803308800385922015-02-01T13:12:00.004-08:002015-03-04T05:07:30.251-08:00Adding Template Classes to Visual Studio 2013's "Add New Item" Screen (NUnit Test Class)When using Visual Studio, if you follow Test Driven Development principles, then you're probably used to repeatedly following these steps when adding a new test class (<i>using NUnit as an example</i>):<br />
<br />
<ol>
<li>Right click on Solution Explorer</li>
<li>Click <i>Add </i> and then <i>New Item</i></li>
<li>Selecting <i>Class</i> from the list of items</li>
<li>Entering your test class name</li>
<li>Typing in or pasting similar looking lines of code:</li>
</ol>
<div>
</div>
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> NUnit.Framework;
<span style="color: blue;">namespace</span> MyProject
{
[<span style="color: #2b91af;">TestFixture</span>]
<span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">MyTestClass</span>
{
[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Test()
{
}
}
}
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><a name='more'></a>
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
Admittedly it's not completely onerous, but it can be a bit of a chore. Thankfully it's very easy to add your own templates to the Add New Item dialog that Visual Studio displays:<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnjWE3_xjk0Nmns7EDObCbgR2exz4jWDfKTqydMp8RfrheU6ttsi1Lhv0guNuOS_OYw9yNgPC3Io_syif_nE0_lFLfkl2Zp5sy96gnoucms_RcUS_MiqCEmTXYcBt_mqZ5odb9/s1600/AddNewTest.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnjWE3_xjk0Nmns7EDObCbgR2exz4jWDfKTqydMp8RfrheU6ttsi1Lhv0guNuOS_OYw9yNgPC3Io_syif_nE0_lFLfkl2Zp5sy96gnoucms_RcUS_MiqCEmTXYcBt_mqZ5odb9/s1600/AddNewTest.jpg" height="353" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtu8sOSQ6yZwbnm76QWNhhWGLRpSnI9bNsbB90ZKn_AFA2s0DyjQjqPO0n95LjsDioAnIeGnx5kugB_1f-CGJY2t0Z_H9MV3vv-KRYF0UNKU0P_Gk7EoE33zcisfFBJovW_tod/s1600/AddNewTestSetup.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtu8sOSQ6yZwbnm76QWNhhWGLRpSnI9bNsbB90ZKn_AFA2s0DyjQjqPO0n95LjsDioAnIeGnx5kugB_1f-CGJY2t0Z_H9MV3vv-KRYF0UNKU0P_Gk7EoE33zcisfFBJovW_tod/s1600/AddNewTestSetup.jpg" height="353" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
Using MSDN's <a href="https://msdn.microsoft.com/en-us/library/tsyyf0yh.aspx">How to: Create Item Templates</a> as guideline, I'm going to create two Item Templates - a standard NUnit test class and one that addtionally includes Setup and Teardown methods.<br />
<br />
<h3>
<b>Getting Started </b></h3>
<div>
<br />
1. Create a new Visual Studio project (it doesn't matter which type as we won't be compiling anything). I've called my project <i>NUnitTemplate</i>.<br />
<br />
2. Add a reference to NUnit or run Install-Package NUnit into Package Manager console. <br />
<br />
3. Add a new class, NUnitTestClass.cs
<br />
<br />
4. Enter the following '<i>code</i>':<br />
<div>
<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> NUnit.Framework;
<span style="color: blue;">namespace</span> $rootnamespace$
{
[TestFixture]
<span style="color: blue;">public</span> <span style="color: blue;">class</span> $<span style="color: red;">safeitemname</span>$
{
[Test]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Test()
{
}
}
}</pre>
<span style="background-color: white;">
</span></pre>
This 'class' looks familiar, but it includes a number of <a href="https://msdn.microsoft.com/en-us/library/ms185311.aspx" target="_blank">template patterns</a> which Visual Studio replaces when it creates the class. More <a href="https://msdn.microsoft.com/en-us/library/eehb4faa.aspx" target="_blank">examples</a> of standard template pattern items.<br />
<br />
5. From the <i>File</i> menu choose <i>Export Template</i>. This starts a wizard that automates the steps required to create a zip file containing your template file, icon, preview image and description for Visual Studio. This file is placed into a specific folder which VS reads at start-up.<br />
<br />
You'll be shown the first page of the Export Template Wizard:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvlFK_dknHcwO7MyJqJnjAUZzsJ-fOSqFtpKaGmiFeuIzBhZA0BzU8FoBQK2lo9toatevayQ8xP0GAORyQhgsE1-u1d-uk3rcxbwaV2Ni3SWUorFcspEV9ZtL6MQI95QnvPFPf/s1600/step5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvlFK_dknHcwO7MyJqJnjAUZzsJ-fOSqFtpKaGmiFeuIzBhZA0BzU8FoBQK2lo9toatevayQ8xP0GAORyQhgsE1-u1d-uk3rcxbwaV2Ni3SWUorFcspEV9ZtL6MQI95QnvPFPf/s1600/step5.jpg" height="301" width="400" /></a></div>
<br />
Choose <i>Item Template</i> as we're creating a new Item, rather than a Project template. You should see only one project listed if you created a single project - <i>NUnitTemplate</i> in my case. Click <i>Next</i>.<br />
<br />
6. On the next <i>Select Item</i> screen you can choose which template item to export - unfortunately you can't choose multiple items to export, which is quite annoying if you've got a few related template items like I have in this demo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTIs1oUODpr_hg8oBPAgT9MDpI-S8jb05ik-YNKZJgLBElIwm9NQYAx26HIHdMkp4eT6p23kSXJzLRyLj04MriFjnz6q8ww_3ICjgNi2Drb925KV53JFHikgubqk-HbfXrJB42/s1600/step6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTIs1oUODpr_hg8oBPAgT9MDpI-S8jb05ik-YNKZJgLBElIwm9NQYAx26HIHdMkp4eT6p23kSXJzLRyLj04MriFjnz6q8ww_3ICjgNi2Drb925KV53JFHikgubqk-HbfXrJB42/s1600/step6.jpg" height="301" width="400" /></a></div>
<br />
Select the item template that you've just added (NUnitTestClass.cs) and click <i>Next</i>.<br />
<br />
7. The <i>Item Reference </i>screen allows you to specify which references needed to be added to the destination project. System is generally needed, but also you'll need NUnit.Framework:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2-W7ux-Uwa0jwNl2FQZX6Au5K8ZmGi-MAEtR5i68_OgQh-zxauadFp9hFd84D5aIaFEOWEC0prLXIeUs5XsHLrWiXh0F-4AGFxp4bQJMxjVpFNFPeu4ZMOEkbxNrRl_GmdnMG/s1600/step7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2-W7ux-Uwa0jwNl2FQZX6Au5K8ZmGi-MAEtR5i68_OgQh-zxauadFp9hFd84D5aIaFEOWEC0prLXIeUs5XsHLrWiXh0F-4AGFxp4bQJMxjVpFNFPeu4ZMOEkbxNrRl_GmdnMG/s1600/step7.jpg" height="301" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
If the destination project doesn't already have a reference to NUnit.framework or Visual Studio cannot locate the registered .DLL then you'll be shown the following warning when you add the item from this template:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4OfdanKeUr7K-G0Vo0ETtKil3jXTYHJMk-EdYY9VbbzSqItE0yXbn5ZIPE-w_E22wHiIRN36XeT-9o8SyPwP-Hecij5CNb0kEH5G5sPh2AARO3osMmkf8AgXtGirWmTYCr7D3/s1600/reference.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4OfdanKeUr7K-G0Vo0ETtKil3jXTYHJMk-EdYY9VbbzSqItE0yXbn5ZIPE-w_E22wHiIRN36XeT-9o8SyPwP-Hecij5CNb0kEH5G5sPh2AARO3osMmkf8AgXtGirWmTYCr7D3/s1600/reference.jpg" height="148" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
8. The last step of the Wizard is to enter some details about this template item:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6e-CAdyMROBq65nbBgo_77FptTkaM5w6LDHmIfDUcT-ZavsgPDWOxDn6cpKHpwkpW8ClqKIXSoiYx1nupk7l3Z19AcH7LMID3cFkQrdHqk5AHC7t_uofR8_Yonp-SX_DIIRL2/s1600/step8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6e-CAdyMROBq65nbBgo_77FptTkaM5w6LDHmIfDUcT-ZavsgPDWOxDn6cpKHpwkpW8ClqKIXSoiYx1nupk7l3Z19AcH7LMID3cFkQrdHqk5AHC7t_uofR8_Yonp-SX_DIIRL2/s1600/step8.jpg" height="301" width="400" /></a></div>
<br />
Template Name is used to create the destination Zip file. <br />
Template Description is shown in right hand pane.<br />
Icon image is shown in the scrollable list of items (I'm pointing at the NUnit .ICO file)<br />
Preview Image is shown in the right hand pane. You need to ensure this is no more 200 pixels wide otherwise VS will compress it to fit into the screen.<br />
<br />
9. Click <i>Finish</i> and the items are copied into a ZIP file using the Template name and placed into a folder within <i>Documents\Visual Studio 2013\My Exported Templates</i>. In my case that's:<br />
<br />
<blockquote class="tr_bq">
<i> C:\Users\jason_000\Documents\Visual Studio 2013\My Exported Templates</i></blockquote>
<br />
Tick <i>Automatically import the template into Visual Studio</i> and VS will copy the file into another folder where VS looks for user defined templates, for me that's:<br />
<br />
<blockquote class="tr_bq">
<i>C:\Users\jason_000\Documents\Visual Studio 2013\Templates\ItemTemplates</i></blockquote>
<br />
That's all the automated steps done.<br />
<br />
10. There's one extra convenience step that VS doesn't do for you - you'll need to alter the <i>DefaultName</i> element from <i>NUnitTestClass</i> to something like <i><DefaultName>Test.cs</DefaultName></i><br />
<br />
Jump to your<i> Documents\Visual Studio 2013\Templates\ItemTemplates</i> folder. Open the NUnitTestClass.zip file:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7YpdqrfEDu_R3ru1yofCZlptLN68Bej96OAx4G94TdnoJSswA-YVa1K-OT23GLXaxVe79zTlm4CNCT81b0VpIuU-QjnjekIhaZI80C0KhHfPuy8xr1vXPEvbLrw3apZOx4s8F/s1600/step9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7YpdqrfEDu_R3ru1yofCZlptLN68Bej96OAx4G94TdnoJSswA-YVa1K-OT23GLXaxVe79zTlm4CNCT81b0VpIuU-QjnjekIhaZI80C0KhHfPuy8xr1vXPEvbLrw3apZOx4s8F/s1600/step9.jpg" height="160" width="400" /></a></div>
<br />
You'll see a file titled <i>MyTemplate.vstemplate</i> - this tells VS how to show/handle the selected item type. Open that in Notepad (or VS):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLxP6h5vgiDE2WGx0pTIfeisqs3zxMLRzvE8PmYX5AZsuPNUp0bMGJGcWoxGSUKV0ypoSCMiLNzOAIzzIu5xPGK4ZbiLzx4mnq8FqQQfN6y8Llj894-bW8oCv3E0DNjjdqgmB8/s1600/step10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLxP6h5vgiDE2WGx0pTIfeisqs3zxMLRzvE8PmYX5AZsuPNUp0bMGJGcWoxGSUKV0ypoSCMiLNzOAIzzIu5xPGK4ZbiLzx4mnq8FqQQfN6y8Llj894-bW8oCv3E0DNjjdqgmB8/s1600/step10.jpg" height="291" width="640" /></a></div>
<br />
Change the <i>DefaultName </i>attribute and save the ZIP file (remembering to copy the Zip file to your My Exported Templates if you're using that folder as the original source).<br />
<br />
<br />
I've repeated the above steps to additionally create a standard test class which includes Setup and Teardown methods, using this template class:<br />
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> NUnit.Framework;
<span style="color: blue;">namespace</span> $rootnamespace$
{
[TestFixture]
<span style="color: blue;">public</span> <span style="color: blue;">class</span> $<span style="color: red;">safeitemname</span>$
{
[<span style="color: red;">SetUp</span>]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> BeforeEachTest()
{
}
[<span style="color: red;">TearDown</span>]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> AfterEachTest()
{
}
[Test]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Test()
{
}
}
}</pre>
<br />
If you feel like following these steps, the final .ZIP files can be downloaded from my site and saved to your <i>Documents\Visual Studio 2013\Templates\ItemTemplates</i> folder.<br />
<br />
<br />
<iframe frameborder="0" height="128" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2116992&authkey=ANVLUonjRaSbelY" width="165"></iframe></div>
</div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com5tag:blogger.com,1999:blog-24402781.post-54527626085702026502015-01-28T13:25:00.000-08:002015-03-04T05:07:50.310-08:00Performance Testing Framework<div class="MsoNormal">
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="MsoNormal">
<div class="MsoNormal">
In this post I’ll introduce a set of performance testing classes
and practices that I use when creating automated performance tests – with one main
aim: simplify the task of measuring/verifying the performance of key areas of
my .NET applications.<o:p></o:p><br />
<br />
Source Code:<br />
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2116991&authkey=APP5ZMyg--wtqEU" width="98"></iframe>
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Often it is not practical, nor even recommended to measure
all parts of an application – why waste time profiling sections of code that
get called infrequently? It’s also rather
difficult knowing exactly where to start profiling an application, whether it’s
server or UI, when that app either hasn’t had performance proactively “baked
in” or when you’re trying to find the root cause of an occasional freeze. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If I’m trying to isolate performance issues (or maybe
retrofitting performance tests), then I tend to start off by using any of the popular
. NET profiling apps, such as Red Gate’s <i><a href="http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/">ANTS</a>.
</i>These give me an idea of where to
look, eg for methods that are called very frequently or take a while to
run. At least this gives me a fighting
chance of where to concentrate my efforts, rather than wading through 1000s of
lines of code…needle in a hay stack springs to mind.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
There are many resources available showing common coding
pitfalls, which can result in extra memory allocates that aren’t needed, making
non-deterministic functions behave in a deterministic fashion, lack of caching,
multiple enumerations etc. Such examples
include:</div>
<div class="MsoNormal">
</div>
<ul>
<li>string concatenation rather than using a StringBuilder, </li>
<li>switch versus multiple if statements, </li>
<li>caching values for ToString/GetHashCode on immutable types</li>
</ul>
<br />
<a name='more'></a><br /><br />
<div>
…the list goes on. </div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ll not delve further into the specifics …will leave that for another
post; another day.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I treat performance tests as being just as important as my
unit tests (which, to me, are of equal importance to my actual project’s
code). The performance tests must be
easy to run, allowing me to tweak the code under test, run the performance
test, and should tell me as soon as a timing metric has not been met…a red alert
followed by a fail.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
If you’re familiar with the <i>red/green/refactor</i> mantra from the Test Driven Development world,
then you’re probably used to seeing red/green alerts whilst you work through a
project.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Initial Benchmarks<o:p></o:p></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
When I’m looking at improving performance for a block of
code and I have no timing metrics, aka key performance indicators, to work with
then I follow these steps:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
</div>
<ol>
<li>Create a performance test that calls the specific method/block - initially with an expected duration of zero milliseconds – this will fail when ran</li>
<li>Run the performance test (which will repeatedly run my method/block under test) to get an idea of current average time (later I’ll discuss why average is not always the best statistic to use)</li>
<li>Bake that first result into my test</li>
<li>Start the process of incremental refactoring/re-running test to quantifiably improve performance</li>
</ol>
<br />
<div class="MsoListParagraphCxSpLast" style="mso-list: l0 level1 lfo1; text-indent: -18.0pt;">
<o:p></o:p></div>
<div class="MsoNormal">
By following these steps, I have an initial benchmark to
work from and will know pretty quickly if I’m actually making things worse, or
if the incremental changes are starting to make less of an impact on the
results (when this happens it’s generally time to stop trying to squeeze the last
clock tick out of a method). <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The benchmark can then serve as future indicator of
performance. If, sometime later, a developer
makes a change to an area of code which negatively affects the performance, the
performance test will fail - much like a failing unit test.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Sampling<o:p></o:p></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Unfortunately, there are many factors that can affect how
long a section of code takes to run, eg JIT overhead, other applications running,
IO activity, debug/release build, physical/virtual memory, type of hard drive,
number and speed of processors. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
All of these come into play and unfortunately it’s not
possible to eradicate any as a factor of the timing stats. To try to alleviate this, the performance
test should run the code under test a number of times and calculate a
meaningful statistic from that. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<i>Gotcha</i>: This
problem is made even worse if you run the performance tests as part of a
Continuous Integration on a build machine.
Your build machine may be used by other dev teams or may be lower spec
than that of your development or end user machines. Performance tests that run without failing on
your development machines may well fail on a build machine.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Running Tests<o:p></o:p></b></div>
<div class="MsoNormal">
When I’m working through a new feature or refactoring
existing code, then it’s important that I can tweak some code and easily run my
tests (whether they’re unit or performance tests). To help I use <a href="https://www.jetbrains.com/resharper/">Resharper</a> from JetBrains. This Visual Studio productivity tool makes
it’s so easy to run/debug a test (unfortunately not all editions of Visual
Studio allow you to create unit/stress/performance tests), simply by clicking
on the test method, using test explorer or re-running as part of a test
session:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX7_pqUzojsFiNsNiVHofDuWL3b0SlLo8TeBS128xO5wMsIdIO0Q6SQl9Fz1ciaOBOVthGqoB_AGAoF51XcmQjZDiiqDelSY0SjvDgLy5rcxxJQqCUHyFjNU_YwNDofryOl3rN/s1600/resharper.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX7_pqUzojsFiNsNiVHofDuWL3b0SlLo8TeBS128xO5wMsIdIO0Q6SQl9Fz1ciaOBOVthGqoB_AGAoF51XcmQjZDiiqDelSY0SjvDgLy5rcxxJQqCUHyFjNU_YwNDofryOl3rN/s1600/resharper.jpg" height="501" width="640" /></a><br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Getting Started</b><o:p></o:p></div>
<div class="MsoNormal">
The key tasks of the performance test base class are:<o:p></o:p></div>
<div class="MsoListParagraphCxSpFirst" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Specify a minimum acceptable time span that a
test method should take to run <br />
<i>protected abstract TimeSpan
AcceptableDuration { get; }<br />
<!--[if !supportLineBreakNewLine]--><br />
<!--[endif]--></i><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Optionally run some <b>untimed</b> fixture setup code, prior to my test cycle running<br />
<i>protected virtual void FixtureSetup()<br />
</i> <o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Optionally run some <b>untimed</b> code prior to running each test case method, maybe to
reduce impact of first JIT operations:<br />
<i>protected virtual void PreTestCaseSetup()</i>
<br />
<!--[if !supportLineBreakNewLine]--><br />
<!--[endif]--><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Run a specific <b>timed</b> test method a repeated number of times, collecting timing
stats from each call:<br />
<i>protected abstract void TestCase()<br />
</i><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Optionally run some <b>untimed</b> code after my test cycle runs, maybe to clean up my
environment:<br />
<i>protected virtual void FixtureTearDown()</i><o:p></o:p></div>
<div class="MsoListParagraphCxSpMiddle" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Calculate some stats from the timings collected<br />
<br /></div>
<div class="MsoListParagraphCxSpLast" style="mso-list: l1 level1 lfo3; text-indent: -18.0pt;">
<!--[if !supportLists]--><span style="font-family: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol;">·<span style="font-family: 'Times New Roman'; font-size: 7pt; font-stretch: normal;">
</span></span><!--[endif]-->Throw an exception if the average time to
complete is greater than the <i>AcceptableDuration</i>
property<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’m going to use the NUnit framework to throw an exception,
via Assert.Fail if the timespan is too long, so you’ll need to run the
following in NuGet Package Manager Console:<o:p></o:p></div>
<div class="MsoNormal" style="background: white; margin-bottom: 0.0001pt;">
<span style="font-family: 'Courier New'; font-size: 10pt;">Install-Package NUnit<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
As it happens, I could have just thrown an exception to
indicate an error state for the test, but NUnit has so many useful features
I’ll stick with the using NUnit’s Assert class.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Starting to look at the code, I’ve created a new <b>Class Library</b> project called
PerformanceTests (which has the NUnit package referenced above) and added a new
class called <i>PerformanceTestBase.cs</i> <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
This is an abstract class, which I’ll base each of my
specific performance tests on – so one test class for each method/block under
test. As an example, say I’ve
established that the GetHashCode function on my <i>Customer</i> class needs to be profiled, then I’d create a class called
CustomerGetHashCodeTest – which will contain all of the code to test just
GetHashCode.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Collections.Generic;
<span style="color: blue;">using</span> System.Diagnostics;
<span style="color: blue;">using</span> System.Linq;
<span style="color: blue;">using</span> System.Text;
<span style="color: blue;">using</span> NUnit.Framework;
<span style="color: blue;">internal</span> <span style="color: blue;">abstract</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">PerformanceTestBase</span>
{
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> The default number of tests cycles to run in order to calc avg</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> DefaultTestCycles = 100;
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> How long each test cycle took to complete</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">List</span><<span style="color: #2b91af;">TimeSpan</span>> _testCycleDurations = <span style="color: blue;">new</span> <span style="color: #2b91af;">List</span><<span style="color: #2b91af;">TimeSpan</span>>();
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> Override if you have specific number of times you want</span>
<span style="color: grey;">///</span><span style="color: green;"> a test to be executed</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">virtual</span> <span style="color: blue;">int</span> TestCyclesToRun
{
<span style="color: blue;">get</span> { <span style="color: blue;">return</span> DefaultTestCycles; }
}
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> The minimum amount of time that we'll allow before failing a test</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">abstract</span> <span style="color: #2b91af;">TimeSpan</span> AcceptableDuration { <span style="color: blue;">get</span>; }
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> TIMED: The actual test that you want to run per cycle.</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">abstract</span> <span style="color: blue;">void</span> TestCase();
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> If you have any code that needs to be ran prior to a test, </span>
<span style="color: grey;">///</span><span style="color: green;"> eg to force JITting </span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">virtual</span> <span style="color: blue;">void</span> PreTestCaseSetup()
{}
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> Override if you need an initial setup operation before </span>
<span style="color: grey;">///</span><span style="color: green;"> the cycle test starts</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">virtual</span> <span style="color: blue;">void</span> FixtureSetup()
{}
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> Override if you need to run clean-up operation after </span>
<span style="color: grey;">///</span><span style="color: green;"> of the cycles have completed</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">protected</span> <span style="color: blue;">virtual</span> <span style="color: blue;">void</span> FixtureTearDown()
{}</pre>
</pre>
<div class="MsoNormal">
}<o:p></o:p></div>
<div class="MsoNormal">
<br />
As you can see there are a mixture of abstract and virtual
methods. When you implement a
performance test you’ll need to implement the following:<o:p></o:p></div>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">abstract</span><span style="font-family: Consolas;"> </span><span style="color: #2b91af; font-family: Consolas;">TimeSpan</span><span style="font-family: Consolas;"> AcceptableDuration { </span><span style="color: blue; font-family: Consolas;">get</span><span style="font-family: Consolas;">; }<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">Returns the minimum acceptable average timespan that will result in a failure of this test</span></pre>
<pre style="background: white;"><span style="color: blue; font-family: Consolas;">protected</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">abstract</span><span style="font-family: Consolas;"> </span><span style="color: blue; font-family: Consolas;">void</span><span style="font-family: Consolas;"> TestCase();<o:p></o:p></span></pre>
<pre style="background: white;"><span style="font-family: "Calibri",sans-serif; font-size: 11.0pt; mso-ascii-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">Contains the actual test code to run – this method call is timed and is used to calculate an overall average.</span></pre>
<div class="MsoNormal">
Now we need to add code for the controlling method, ensuring
that it’s accessible by any NUnit test runners.
<i>TestCaseRunner</i> is a public
void method, annotated with the NUnit <i>Test</i>
attribute:<o:p></o:p></div>
<div class="MsoNormal">
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;">[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> TestCaseRunner()
{
FixtureSetup();
<span style="color: blue;">var</span> testCycles = TestCyclesToRun;
<span style="color: blue;">var</span> timer = <span style="color: blue;">new</span> <span style="color: #2b91af;">Stopwatch</span>();
Log(<span style="color: #a31515;">"STARTING TEST. Cycles={0}, AcceptableDuration={1}"</span>,
testCycles, FormatTimeSpan(AcceptableDuration));
Log(<span style="color: #a31515;">"CYCLE \tTime"</span>);
<span style="color: blue;">for</span> (<span style="color: blue;">var</span> cycle = 1; cycle <= testCycles; cycle++)
{
PreTestCaseSetup();
timer.Start();
TestCase(); <span style="color: green;">// run the actual test</span>
timer.Stop();
<span style="color: blue;">var</span> elapsed = timer.Elapsed;
_testCycleDurations.Add(elapsed);
Log(<span style="color: #a31515;">"{0:000}\t{1}"</span>, cycle, FormatTimeSpan(elapsed));
timer.Reset();
}
FixtureTearDown();
AssertStatistics();
}</pre>
</pre>
</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
We use a StopWatch instance to provide the actual timing
element, simply starting, stopping and resetting the timer between each cycle –
this provides a high-enough resolution.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The remaining <i><span style="font-family: Consolas;">Log</span></i> and <i>AssertStatistics</i> methods need to be
defined. Log is simply a helper to log
messages to the console.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">void</span> AssertStatistics()
{
<span style="color: blue;">var</span> average = _testCycleDurations.AverageTotalMilliseconds();
<span style="color: blue;">var</span> min = _testCycleDurations.MinTotalMilliseconds();
<span style="color: blue;">var</span> max = _testCycleDurations.MaxTotalMilliseconds();
<span style="color: blue;">var</span> deviation = _testCycleDurations.Deviation();
<span style="color: blue;">const</span> <span style="color: blue;">double</span> Percentile = 95D;
<span style="color: blue;">var</span> percentile = _testCycleDurations.Percentile(Percentile);
<span style="color: blue;">var</span> acceptableDuration = AcceptableDuration;
Log(<span style="color: #a31515;">"TEST FINISHED. Timings(ms): Acceptable={0}, Percentile={1}, Avg={2}, Min={3}, Max={4}, Deviation={5}, ActualTimes={6}"</span>,
FormatTimeSpan(acceptableDuration),
FormatTimeSpan(percentile), FormatTimeSpan(average),
FormatTimeSpan(min), FormatTimeSpan(max),
FormatTimeSpan(deviation),
Summarise(_testCycleDurations));
<span style="color: blue;">var</span> testPassed = percentile <= acceptableDuration;
<span style="color: blue;">if</span> (!testPassed)
{
<span style="color: #2b91af;">Assert</span>.Fail(<span style="color: #a31515;">"DURATION EXCEEDED: Acceptable=</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">, Percentile=</span><span style="color: mediumseagreen;">{1}</span><span style="color: #a31515;">"</span>,
FormatTimeSpan(acceptableDuration), FormatTimeSpan(percentile));
}
}</pre>
</pre>
<div class="MsoNormal">
<span style="font-family: Consolas;">AssertStatistics()</span>
uses a few extensions methods that
extend<i> ICollection<TimeSpan></i>
from a class called TimeSpanCollectionExtensions.cs (below) . These methods determine the minimum, maximum,
average, deviation and most importantly the Percentile value from the timing
stats collected after each cycle.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
In order to determine if we’ve exceeded the acceptable time
space, compare that with use the 95<sup>th</sup> percentile – this is more
accurate than using the average value.<span style="background-color: white; font-family: Consolas;"> </span></div>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<div class="MsoNormal">
The remaining methods help with formatting the results:<span style="background-color: white; font-family: Consolas;"> </span></div>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FormatTimeSpan(<span style="color: #2b91af;">TimeSpan</span> span)
{
<span style="color: blue;">return</span> span.TotalMilliseconds.ToString(<span style="color: #a31515;">"N0"</span>);
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> Summarise(<span style="color: #2b91af;">IEnumerable</span><<span style="color: #2b91af;">TimeSpan</span>> durations)
{
<span style="color: blue;">return</span> durations
.Aggregate(<span style="color: blue;">new</span> <span style="color: #2b91af;">StringBuilder</span>(),
(bld, ts) => bld.Append(<span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">|"</span>, FormatTimeSpan(ts))))
.ToString();
}
<span style="color: blue;">protected</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Log(<span style="color: blue;">string</span> format, <span style="color: blue;">params</span> <span style="color: blue;">object</span>[] args)
{
<span style="color: blue;">var</span> msg = <span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0:HH:mm:ss.fff}</span><span style="color: #a31515;">: </span><span style="color: mediumseagreen;">{1}</span><span style="color: #a31515;">"</span>,
<span style="color: #2b91af;">DateTime</span>.Now, <span style="color: blue;">string</span>.Format(format, args));
<span style="color: #2b91af;">Debug</span>.WriteLine(msg);
}</pre>
</pre>
<pre style="background: white;"><span style="font-family: Consolas;"> </span></pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Finally TimeSpanCollectionExtensions.cs. These methods haven’t been profiled:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Collections.Generic;
<span style="color: blue;">using</span> System.Linq;
<span style="color: blue;">internal</span> <span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">TimeSpanCollectionExtensions</span>
{
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">TimeSpan</span> Percentile(<span style="color: blue;">this</span> <span style="color: #2b91af;">ICollection</span><<span style="color: #2b91af;">TimeSpan</span>> testDurations, <span style="color: blue;">double</span> percentile)
{
<span style="color: blue;">var</span> sortedDurations = <span style="color: blue;">new</span> <span style="color: #2b91af;">List</span><<span style="color: #2b91af;">TimeSpan</span>>(testDurations);
sortedDurations.Sort();
<span style="color: blue;">var</span> percentileIndex = <span style="color: #2b91af;">Convert</span>.ToInt32((percentile / 100D) * sortedDurations.Count);
<span style="color: blue;">return</span> sortedDurations.ElementAt(percentileIndex);
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">TimeSpan</span> Deviation(<span style="color: blue;">this</span> <span style="color: #2b91af;">ICollection</span><<span style="color: #2b91af;">TimeSpan</span>> testCycleDurations)
{
<span style="color: blue;">var</span> sortedDurations = <span style="color: blue;">new</span> <span style="color: #2b91af;">List</span><<span style="color: #2b91af;">TimeSpan</span>>(testCycleDurations);
sortedDurations.Sort();
<span style="color: blue;">var</span> minTime = sortedDurations.First();
<span style="color: blue;">var</span> maxTime = sortedDurations.Last();
<span style="color: blue;">if</span> (minTime != maxTime)
{
sortedDurations.Remove(minTime);
sortedDurations.Remove(maxTime);
}
<span style="color: blue;">return</span> <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(sortedDurations.Average(ts => ts.TotalMilliseconds));
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">TimeSpan</span> AverageTotalMilliseconds(<span style="color: blue;">this</span> <span style="color: #2b91af;">ICollection</span><<span style="color: #2b91af;">TimeSpan</span>> testDurations)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(
testDurations.Average(ts => ts.TotalMilliseconds));
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">TimeSpan</span> MinTotalMilliseconds(<span style="color: blue;">this</span> <span style="color: #2b91af;">ICollection</span><<span style="color: #2b91af;">TimeSpan</span>> testDurations)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(
testDurations.Min(ts => ts.TotalMilliseconds));
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">TimeSpan</span> MaxTotalMilliseconds(<span style="color: blue;">this</span> <span style="color: #2b91af;">ICollection</span><<span style="color: #2b91af;">TimeSpan</span>> testDurations)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(
testDurations.Max(ts => ts.TotalMilliseconds));
}
}
</pre>
</pre>
<div class="MsoNormal">
<b>Creating a
Performance Test<o:p></o:p></b></div>
<div class="MsoNormal">
Before we start, I need to create something to test. So, I’ve added a new Class Library project
(SampleApp). To that, I’ll add a NumberWords
class:<o:p></o:p></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">using</span> System;
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">NumberWords</span>
{
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FromSwitch(<span style="color: blue;">int</span> number)
{
<span style="color: blue;">switch</span> (number)
{
<span style="color: blue;">case</span> 0: <span style="color: blue;">return</span> <span style="color: #a31515;">"zero-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">case</span> 1: <span style="color: blue;">return</span> <span style="color: #a31515;">"one-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
......</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"> <span style="color: blue;">case</span> 49: <span style="color: blue;">return</span> <span style="color: #a31515;">"fortynine-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">case</span> 50: <span style="color: blue;">return</span> <span style="color: #a31515;">"fifty-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">default</span>: <span style="color: blue;">return</span> <span style="color: #a31515;">"??-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
}
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FromIfs(<span style="color: blue;">int</span> number)
{
<span style="color: blue;">if</span> (number == 0) <span style="color: blue;">return</span> <span style="color: #a31515;">"zero-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">if</span> (number == 1) <span style="color: blue;">return</span> <span style="color: #a31515;">"one-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
......</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"> <span style="color: blue;">if</span> (number == 49) <span style="color: blue;">return</span> <span style="color: #a31515;">"fortynine-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">if</span> (number == 50) <span style="color: blue;">return</span> <span style="color: #a31515;">"fifty-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
<span style="color: blue;">return</span> <span style="color: #a31515;">"??-"</span> + <span style="color: #2b91af;">Environment</span>.TickCount;
}
}
</pre>
</pre>
</pre>
<div class="MsoNormal">
FromSwitch() and FromIfs() return exactly the same thing,
they highlight the performance difference you might see between the two
implementations.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ll start with creating a performance test for the FromSwitch
method (I’ll need another test class for FromIfs too). Add a new class to the PerformanceTests
project NumberWordsFromIfsTest.cs:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Linq;
<span style="color: blue;">using</span> NUnit.Framework;
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">NumberWordTestCriteria</span>
{
<span style="color: blue;">public</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> Start = 0;
<span style="color: blue;">public</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> Count = 50000;
<span style="color: blue;">public</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> AcceptableMiliseconds = 90;
}
[<span style="color: #2b91af;">TestFixture</span>]
<span style="color: blue;">class</span> <span style="color: #2b91af;">NumberWordsFromIfsTest</span> : <span style="color: #2b91af;">PerformanceTestBase</span>
{
<span style="color: blue;">protected</span> <span style="color: blue;">override</span> <span style="color: #2b91af;">TimeSpan</span> AcceptableDuration
{
<span style="color: blue;">get</span> { <span style="color: blue;">return</span> <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(<span style="color: #2b91af;">NumberWordTestCriteria</span>.AcceptableMiliseconds); }
}
<span style="color: blue;">protected</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> PreTestCaseSetup()
{
<span style="color: blue;">var</span> num = GetWord(0);
}
<span style="color: blue;">protected</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> TestCase()
{
<span style="color: blue;">var</span> words = <span style="color: #2b91af;">Enumerable</span>.Range(<span style="color: #2b91af;">NumberWordTestCriteria</span>.Start, <span style="color: #2b91af;">NumberWordTestCriteria</span>.Count)
.Select(GetWord)
.ToList();
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> GetWord(<span style="color: blue;">int</span> number)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">NumberWords</span>.FromIfs(number);
}
}
</pre>
</pre>
<div class="MsoNormal">
NumberWordTestCriteria is a static class that defines some
parameters for the tests – I’ve added this class as I want to use the same
criteria for the next NumberWordsFromSwitchTest.cs <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Looking at NumberWordsFromIfsTest, the class derives from our
base performance class PerformanceTestBase and is decorated with the TestFixture
attribute so that NUnit runners can find the test.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
The abstract AcceptableDuration property is implemented – I want
this to pass if the average time span is less than 90 milliseconds.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ve also implemented <b><i>PreTestCaseSetup</i></b>(), this will get
ran prior to running my TestCase method – the method under test. You can generally use this approach to negate
the effect of CLR JITing when a method is first called.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
TestCase is the method that gets called repeatedly and its
execution time averaged out to see if I’m improving or degrading performance. <o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Using Resharper I can quickly run the performance test:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0ec4EyXbQUu_IpigWvh4Sm_d08A3phbHZWfb-B2LZ-JtI3zN8dQ28BaiPDhgWTmvOEb4orM6slZ3wLSYu8YY1PjuUHrGX1k15t8m1eUFmfXr0umxllsjV0Qml1P9uKnoRcron/s1600/runtest.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0ec4EyXbQUu_IpigWvh4Sm_d08A3phbHZWfb-B2LZ-JtI3zN8dQ28BaiPDhgWTmvOEb4orM6slZ3wLSYu8YY1PjuUHrGX1k15t8m1eUFmfXr0umxllsjV0Qml1P9uKnoRcron/s1600/runtest.jpg" height="330" width="640" /></a><br />
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Once the test run is over, I’ll see a red or green alert and
the following results:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span style="font-family: Courier New, Courier, monospace;">20:19:54.583: STARTING TEST. Cycles=100,
AcceptableDuration=90<br />
20:19:54.620: CYCLE Time<br />
20:19:54.854: 001 114<br />
20:19:54.935: 002 78<br />
20:19:55.019: 003 80<br />
20:19:55.095: 004 73<br />
20:19:55.176: 005 77<br />
…..<br />
20:20:03.308: 095 84<br />
20:20:03.387: 096 76<br />
20:20:03.479: 097 89<br />
20:20:03.575: 098 93<br />
20:20:03.661: 099 83<br />
20:20:03.768: 100 104<o:p></o:p></span></div>
<div class="MsoNormal">
<span style="font-family: Courier New, Courier, monospace;">20:20:03.824: TEST FINISHED. Timings(ms): Acceptable=90,
Percentile=113, Avg=87, Min=70, Max=123, Deviation=87, ActualTimes=114|78|80.</span><o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Unfortunately my performance test failed <span style="font-family: Wingdings; mso-ascii-font-family: Calibri; mso-ascii-theme-font: minor-latin; mso-char-type: symbol; mso-hansi-font-family: Calibri; mso-hansi-theme-font: minor-latin; mso-symbol-font-family: Wingdings;">L</span>. The <span style="font-family: Consolas;">TestCase </span>method was called 100 times, the 95<sup>th</sup> percentile
time was 113 milliseconds, but my acceptable time was only 90 milliseconds<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US" style="color: #1e1e1e; font-family: Consolas; font-size: 10.0pt; line-height: 107%; mso-ansi-language: EN-US;">DURATION EXCEEDED:
Acceptable=90, Percentile=113</span><o:p></o:p></div>
<div class="MsoNormal">
<span lang="EN-US" style="color: #1e1e1e; font-family: Consolas; font-size: 10.0pt; line-height: 107%; mso-ansi-language: EN-US;"><br /></span></div>
<div class="MsoNormal">
So now it’s up to me to find a faster way to concatenate the
words.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b>Going Further<o:p></o:p></b></div>
<div class="MsoNormal">
I’ve added the following JoinWords class to my SampleApp project:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Linq;
<span style="color: blue;">using</span> System.Text;
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">JoinWords</span>
{
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FromStringBuilder(<span style="color: blue;">int</span> count)
{
<span style="color: blue;">var</span> builder = <span style="color: blue;">new</span> <span style="color: #2b91af;">StringBuilder</span>();
<span style="color: blue;">foreach</span> (<span style="color: blue;">var</span> item <span style="color: blue;">in</span> <span style="color: #2b91af;">Enumerable</span>.Range(0, count))
{
builder.AppendLine(item.ToString());
}
<span style="color: blue;">return</span> builder.ToString();
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FromConcatenation(<span style="color: blue;">int</span> count)
{
<span style="color: blue;">var</span> line = <span style="color: blue;">string</span>.Empty;
<span style="color: blue;">foreach</span> (<span style="color: blue;">var</span> item <span style="color: blue;">in</span> <span style="color: #2b91af;">Enumerable</span>.Range(0, count))
{
line += item + <span style="color: #2b91af;">Environment</span>.NewLine;
}
<span style="color: blue;">return</span> line;
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> FromAggregate(<span style="color: blue;">int</span> count)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">Enumerable</span>.Range(0, count)
.Aggregate(<span style="color: blue;">new</span> <span style="color: #2b91af;">StringBuilder</span>(),
(builder, item) => builder.AppendLine(item.ToString()))
.ToString();
}
}</pre>
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Again, each of the three methods do the same thing, they
return a string of numbers joined together, using a StringBuilder, string
concatenation and a StringBuilder in
conjunction the Linq Aggregate method.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
I’ve chosen these implementations to highlight how slow
string concatenation can be. I’ve added
three test classes using the pattern outline above (although I do have an extra
abstract class JoinWordsTestBase that wraps up some commonality)<o:p></o:p></div>
<div class="MsoNormal">
It’s interesting to see just how slow string concatenation runs compared to using the StringBuilder – ten times slower.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<br />
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<o:p> </o:p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiujmsj-KRlHaPjRW0jrr1x7Wl-yW_XnMiAMXT7m69Pke0J6gTFFaLLDSsd_u4Xj_xevSMjHhUTe8_qIuPhPHBgg7GHDRzJ9Tna3oY-uFkc8ncMDIR0ERro24iOfwTOa9PLV9ac/s1600/joinwords.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiujmsj-KRlHaPjRW0jrr1x7Wl-yW_XnMiAMXT7m69Pke0J6gTFFaLLDSsd_u4Xj_xevSMjHhUTe8_qIuPhPHBgg7GHDRzJ9Tna3oY-uFkc8ncMDIR0ERro24iOfwTOa9PLV9ac/s1600/joinwords.jpg" height="464" width="640" /></a></div>
</div>
</div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com11tag:blogger.com,1999:blog-24402781.post-60868234483069011072015-01-23T13:33:00.002-08:002015-03-04T05:08:06.079-08:00Displaying Colour Coded Percentage Columns in WPF XamDataGrid <span style="font-family: Arial, Helvetica, sans-serif;">Infragistic's XamDataGrid is a highly customisable WPF control.</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">I'm going to walk through the steps required to display a number of colour-coded fields on a XamDataGrid, where each field is bound to an underlying percentage based property. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">This is achieved by binding a Brush instance to the Background property of the Field's CellValuePresenter:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFp-NbZR4GrzE6F6zxPughSLm3vLe0uqB9ytYFP8BZYKHlhKO8aKHRjGPGGaHxf593LE-1SpMkSsIe1JOUf_MKFEVS8IzvoSdDkDvz_PoMtASCrUCk32NpMgQ8VC7pBp9QFVC-/s1600/screen.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFp-NbZR4GrzE6F6zxPughSLm3vLe0uqB9ytYFP8BZYKHlhKO8aKHRjGPGGaHxf593LE-1SpMkSsIe1JOUf_MKFEVS8IzvoSdDkDvz_PoMtASCrUCk32NpMgQ8VC7pBp9QFVC-/s1600/screen.jpg" /></a></div>
<br />
<a name='more'></a><br /><br />
I'll start by adding my "% Complete" <b><i>Field </i></b>into the <b><i>FieldLayout.Fields</i></b> of my XamDataGrid collection:<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">Window</span><span style="background-color: white; color: red;"> xmlns</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"http://schemas.microsoft.com/winfx/2006/xaml/presentation"</span><span style="background-color: white;">
</span><span style="background-color: white; color: red;"> xmlns</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: red;">x</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"http://schemas.microsoft.com/winfx/2006/xaml"</span><span style="background-color: white;">
</span><span style="background-color: white; color: red;"> xmlns</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: red;">igWPF</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"http://schemas.infragistics.com/xaml/wpf"</span><span style="background-color: white;">
</span><span style="background-color: white; color: red;"> xmlns</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: red;">igDP</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"http://infragistics.com/DataPresenter"</span><span style="background-color: white;">
</span><span style="background-color: white; color: red;"> </span><span style="background-color: white; color: red;">xmlns</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: red;">igEditors</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"http://infragistics.com/Editors"</span><span style="background-color: white;">
</span><span style="background-color: white; color: red;"> </span><span style="background-color: white; color: red;">xmlns</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: red;">local</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"clr-namespace:XamDataGridPercentField"</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">XamDataGrid</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igDP</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">XamDataGrid.FieldLayouts</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">FieldLayout</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">FieldLayout.Fields</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: #fff2cc;"><span style="color: blue;"><</span><span style="color: #a31515;">igWPF</span><span style="color: blue;">:</span><span style="color: #a31515;">Field</span><span style="color: red;"> Label</span><span style="color: blue;">=</span><span style="color: blue;">"% Complete"</span><span style="color: red;"> Name</span><span style="color: blue;">=</span><span style="color: blue;">"PercentComplete"</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">igWPF</span><span style="color: blue;">:</span><span style="color: #a31515;">Field</span><span style="color: blue;">></span></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">FieldLayout.Fields</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">FieldLayout</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igDP</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">XamDataGrid.FieldLayouts</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">XamDataGrid</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">Window</span><span style="background-color: white; color: blue;">></span></pre>
<span style="font-family: Arial, Helvetica, sans-serif;">In the above snippet <b><i>PercentComplete </i></b>is a property found on my data source (I have a simple list of <i>Project </i>items - bound at runtime to the Window's DataContext)</span><br />
<br />
<span style="font-family: Arial, Helvetica, sans-serif;">Now add a new <b><i>CellBinding </i></b>to the <b><i>Field.CellBindings</i></b> collection, targeting the CellValuePresenter's Background property:</span><br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field</span><span style="background-color: white; color: red;"> Label</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"% Complete"</span><span style="background-color: white; color: red;"> Name</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"PercentComplete"</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: #fff2cc;"><span style="color: blue;"><</span><span style="color: #a31515;">igWPF</span><span style="color: blue;">:</span><span style="color: #a31515;">Field.CellBindings</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">igWPF</span><span style="color: blue;">:</span><span style="color: #a31515;">CellBinding</span><span style="color: red;"> Property</span><span style="color: blue;">=</span><span style="color: blue;">"Background"</span><span style="color: red;"> Target</span><span style="color: blue;">=</span><span style="color: blue;">"CellValuePresenter"</span>
<span style="color: red;">Binding</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> DataItem</span><span style="color: blue;">.</span><span style="color: blue;">PercentComplete</span><span style="color: blue;">,</span>
<span style="color: red;">Converter</span><span style="color: blue;">={</span><span style="color: #a31515;">StaticResource</span><span style="color: red;"> percentToBrushConverter</span><span style="color: blue;">},</span>
<span style="color: red;">TargetNullValue</span><span style="color: blue;">=</span><span style="color: blue;">White</span><span style="color: blue;">,</span><span style="color: red;"> FallbackValue</span><span style="color: blue;">=</span><span style="color: blue;">Silver</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: blue;">/></span>
<span style="color: blue;"></</span><span style="color: #a31515;">igWPF</span><span style="color: blue;">:</span><span style="color: #a31515;">Field.CellBindings</span><span style="color: blue;">></span></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field</span><span style="background-color: white; color: blue;">></span></pre>
<br />
<span style="font-family: Arial, Helvetica, sans-serif;">The value converter, <b><i>percentToBrushConverter </i></b> does the job of converting a double percentage value into a Brush type based on the numeric value - I'll come to that shortly.</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">I'm also going to define a custom EditorStyle, <b><i>percentageFieldStyle , </i></b>which defines how a XamTextEditor control is used to format the text value of the field:</span><br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field</span><span style="background-color: white; color: red;"> Label</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"% Complete"</span><span style="background-color: white; color: red;"> Name</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"PercentComplete"</span><span style="background-color: white;">
</span><span style="color: red;"><span style="background-color: white;"> </span><span style="background-color: #fff2cc;">EditorStyle</span></span><span style="background-color: #fff2cc;"><span style="color: blue;">="{</span><span style="color: #a31515;">StaticResource</span><span style="color: red;"> percentageFieldStyle</span><span style="color: blue;">}</span><span style="color: blue;">"</span></span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field.CellBindings</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;"><</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">CellBinding</span><span style="background-color: white; color: red;"> Property</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"Background"</span><span style="background-color: white; color: red;"> Target</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">"CellValuePresenter"</span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: red;">Binding</span><span style="background-color: white; color: blue;">="{</span><span style="background-color: white; color: #a31515;">Binding</span><span style="background-color: white; color: red;"> DataItem</span><span style="background-color: white; color: blue;">.</span><span style="background-color: white; color: blue;">PercentComplete</span><span style="background-color: white; color: blue;">,</span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: red;">Converter</span><span style="background-color: white; color: blue;">={</span><span style="background-color: white; color: #a31515;">StaticResource</span><span style="background-color: white; color: red;"> percentToBrushConverter</span><span style="background-color: white; color: blue;">},</span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: red;">TargetNullValue</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">White</span><span style="background-color: white; color: blue;">,</span><span style="background-color: white; color: red;"> FallbackValue</span><span style="background-color: white; color: blue;">=</span><span style="background-color: white; color: blue;">Silver</span><span style="background-color: white; color: blue;">}</span><span style="background-color: white; color: blue;">"</span><span style="background-color: white; color: blue;">/></span><span style="background-color: white;">
</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field.CellBindings</span><span style="background-color: white; color: blue;">></span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;"></</span><span style="background-color: white; color: #a31515;">igWPF</span><span style="background-color: white; color: blue;">:</span><span style="background-color: white; color: #a31515;">Field</span><span style="background-color: white; color: blue;">></span></pre>
<span style="font-family: Arial, Helvetica, sans-serif;">Both </span><i style="font-family: Arial, Helvetica, sans-serif;">percentageFieldStyle </i><span style="font-family: Arial, Helvetica, sans-serif;">and </span><i style="font-family: Arial, Helvetica, sans-serif;">percentToBrushConverter </i><span style="font-family: Arial, Helvetica, sans-serif;">are defined in as local resource in my XamDataGrid, read using the StaticResource markup extension</span><br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;"><</span><span style="color: #a31515;">igDP</span><span style="color: blue;">:</span><span style="color: #a31515;">XamDataGrid.Resources</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">local</span><span style="color: blue;">:</span><span style="color: #a31515;">PercentToBrushConverter</span><span style="color: red;"> x</span><span style="color: blue;">:</span><span style="color: red;">Key</span><span style="color: blue;">=</span><span style="color: blue;">"percentToBrushConverter"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Style</span><span style="color: red;"> x</span><span style="color: blue;">:</span><span style="color: red;">Key</span><span style="color: blue;">=</span><span style="color: blue;">"percentageFieldStyle"</span><span style="color: red;"> TargetType</span><span style="color: blue;">=</span><span style="color: blue;">"igEditors:XamTextEditor"</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Setter</span><span style="color: red;"> Property</span><span style="color: blue;">=</span><span style="color: blue;">"Format"</span><span style="color: red;"> Value</span><span style="color: blue;">=</span><span style="color: blue;">"P0"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Setter</span><span style="color: red;"> Property</span><span style="color: blue;">=</span><span style="color: blue;">"HorizontalContentAlignment"</span><span style="color: red;"> Value</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Setter</span><span style="color: red;"> Property</span><span style="color: blue;">=</span><span style="color: blue;">"ToolTip"</span><span style="color: red;"> Value</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> Value</span><span style="color: blue;">,</span><span style="color: red;"> StringFormat</span><span style="color: blue;">=</span><span style="color: blue;">'0.00'</span><span style="color: blue;">,</span><span style="color: red;">
RelativeSource</span><span style="color: blue;">={</span><span style="color: #a31515;">RelativeSource</span><span style="color: red;"> Self</span><span style="color: blue;">}}</span><span style="color: blue;">"</span><span style="color: blue;"> /></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Style</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">igDP</span><span style="color: blue;">:</span><span style="color: #a31515;">XamDataGrid.Resources</span><span style="color: blue;">></span></pre>
<span style="font-family: Arial, Helvetica, sans-serif;">Delving into the brush converter code, I'll start by creating a class <i>PercentColorRanges.
</i>This static </span><span style="font-family: Arial, Helvetica, sans-serif;">class is responsible for determining the background colour to use from a predefined range of colours from the bound % value:</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">static</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">PercentColorRanges</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">ColorRange</span>
{
<span style="color: blue;">public</span> ColorRange(<span style="color: blue;">int</span> min, <span style="color: blue;">int</span> max, <span style="color: #2b91af;">Color</span> color)
{
Min = min;
Max = max;
Color = color;
}
<span style="color: blue;">public</span> <span style="color: blue;">int</span> Min { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: blue;">int</span> Max { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: #2b91af;">Color</span> Color { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">HashSet</span><<span style="color: #2b91af;">ColorRange</span>> _colorRanges = <span style="color: blue;">new</span> <span style="color: #2b91af;">HashSet</span><<span style="color: #2b91af;">ColorRange</span>>()
{
<span style="color: blue;">new</span> <span style="color: #2b91af;">ColorRange</span>(0, 20, <span style="color: #2b91af;">Colors</span>.OrangeRed),
<span style="color: blue;">new</span> <span style="color: #2b91af;">ColorRange</span>(21, 40, <span style="color: #2b91af;">Colors</span>.Orange),
<span style="color: blue;">new</span> <span style="color: #2b91af;">ColorRange</span>(41, 60, <span style="color: #2b91af;">Colors</span>.Yellow),
<span style="color: blue;">new</span> <span style="color: #2b91af;">ColorRange</span>(61, 80, <span style="color: #2b91af;">Colors</span>.YellowGreen),
<span style="color: blue;">new</span> <span style="color: #2b91af;">ColorRange</span>(81, 100, <span style="color: #2b91af;">Colors</span>.Green)
};
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Color</span> DefaultColor = <span style="color: #2b91af;">Colors</span>.Silver;
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">Color</span> GetColor(<span style="color: blue;">int</span> percentage)
{
<span style="color: blue;">var</span> range = _colorRanges.FirstOrDefault(r => percentage >= r.Min &&
percentage <= r.Max);
<span style="color: blue;">return</span> range != <span style="color: blue;">null</span> ? range.Color : DefaultColor;
}
}</pre>
<div style="background-color: white; font-size: 13px;">
<span style="font-family: Arial, Helvetica, sans-serif; font-size: small; white-space: normal;">I'm using a hard-coded range of colours for this demo (a HashSet of type ColorRange), so for example a % value in the range 0-20 will have a colour of OrangeRed, If for some reason our % falls outside of the 0-100 range then we'll use the DefaultColor - Silver. </span></div>
<div style="background-color: white; font-size: 13px;">
<span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size: small; white-space: normal;">Next comes our </span><span style="color: #2b91af;">PercentToBrushConverter </span><span style="font-size: small; white-space: normal;">class. This implements the standard </span><span style="color: #2b91af;">IValueConverter </span><span style="font-size: small; white-space: normal;">interface which WPF uses to convert to and from data values. In order to preserve resources, we'll share instances of the brushes in a local dictionary object (each Brush marked as frozen to save on having to hook into change notifications):</span></span></div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;">[<span style="color: #2b91af;">ValueConversion</span>(<span style="color: blue;">typeof</span>(<span style="color: blue;">double</span>), <span style="color: blue;">typeof</span>(<span style="color: #2b91af;">Brush</span>))]
<span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">PercentToBrushConverter</span> : <span style="color: #2b91af;">IValueConverter</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">IDictionary</span><<span style="color: blue;">int</span>, <span style="color: #2b91af;">Brush</span>> _brushesByPercent =
<span style="color: blue;">new</span> <span style="color: #2b91af;">Dictionary</span><<span style="color: blue;">int</span>, <span style="color: #2b91af;">Brush</span>>();
<span style="color: blue;">public</span> <span style="color: blue;">object</span> Convert(<span style="color: blue;">object</span> value, <span style="color: #2b91af;">Type</span> targetType, <span style="color: blue;">object</span> parameter,
<span style="color: #2b91af;">CultureInfo</span> culture)
{
<span style="color: blue;">var</span> percentageFrac = value <span style="color: blue;">as</span> <span style="color: blue;">double</span>?;
<span style="color: blue;">if</span> (percentageFrac == <span style="color: blue;">null</span> || percentageFrac.GetValueOrDefault() == 0)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">Binding</span>.DoNothing;
}
<span style="color: green;">// Convert to whole number, eg 0.01 = 1</span>
<span style="color: blue;">var</span> percentageValue = percentageFrac.GetValueOrDefault();
<span style="color: blue;">var</span> percentageWhole = (<span style="color: blue;">int</span>)<span style="color: #2b91af;">Math</span>.Round(percentageValue * 100D, 0);
<span style="color: green;">// See if we've already got this in our list of known brushes by %</span>
<span style="color: #2b91af;">Brush</span> brush;
<span style="color: blue;">if</span> (_brushesByPercent.TryGetValue(percentageWhole, <span style="color: blue;">out</span> brush))
{
<span style="color: blue;">return</span> brush;
}
<span style="color: blue;">var</span> color = <span style="color: #2b91af;">PercentColorRanges</span>.GetColor(percentageWhole);
brush = <span style="color: blue;">new</span> <span style="color: #2b91af;">LinearGradientBrush</span>
{
StartPoint = <span style="color: blue;">new</span> <span style="color: #2b91af;">Point</span>(0, 0),
EndPoint = <span style="color: blue;">new</span> <span style="color: #2b91af;">Point</span>(1, 0),
GradientStops = <span style="color: blue;">new</span> <span style="color: #2b91af;">GradientStopCollection</span>(<span style="color: blue;">new</span>[]
{
CreateFrozenGradient(color, percentageValue),
CreateFrozenGradient(<span style="color: #2b91af;">Colors</span>.Transparent, percentageValue)
})
};
brush.Freeze();
_brushesByPercent.Add(percentageWhole, brush);
<span style="color: blue;">return</span> brush;
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">GradientStop</span> CreateFrozenGradient(<span style="color: #2b91af;">Color</span> color,
<span style="color: blue;"> double</span> offset)
{
<span style="color: blue;">var</span> grad = <span style="color: blue;">new</span> <span style="color: #2b91af;">GradientStop</span>(color, offset);
grad.Freeze();
<span style="color: blue;">return</span> grad;
}
<span style="color: blue;">public</span> <span style="color: blue;">object</span> ConvertBack(<span style="color: blue;">object</span> value, <span style="color: #2b91af;">Type</span> targetType, <span style="color: blue;">object</span> parameter,
<span style="color: #2b91af;">CultureInfo</span> culture)
{
<span style="color: blue;">return</span> <span style="color: #2b91af;">Binding</span>.DoNothing;
}
}</pre>
</div>
</pre>
</pre>
<div>
The key to this <i>Converter </i>is the <i>Convert </i>method. We can't convert from a Brush back to a percentage value so <i>ConvertBack </i>simply returns<i> Binding.DoNothing</i> - which instructs the binding engine to not perform any binding action.<br />
<br />
We have a Dictionary of brushes keyed on the percent value. This a small performance optimisation - if we've already created and shared out a brush for a specific numeric value then there's no need to create another brush for that value. Although this does mean we could have up to 100 brush instances in memory.<br />
<br />
For the double value passed to the converter we assume percentages are passed in as decimals, eg 1% = 0.01 through to 100% = 1.0. Although it's an easy task to also handle the case where % values are passed in as integers 1%=1...100%=100.<br />
<br />
We round fractions of a percentage down to the nearest 0 decimal places:<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">var</span> percentageWhole = (<span style="color: blue;">int</span>)<span style="color: #2b91af;">Math</span>.Round(percentageValue * 100D, 0);</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"></pre>
</div>
<div>
Next we take a quick peek into the Dictionary of existing brushes and return the one that is already there...if we have one:</div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: #2b91af;">Brush</span> brush;
<span style="color: blue;">if</span> (_brushesByPercent.TryGetValue(percentageWhole, <span style="color: blue;">out</span> brush))
{
<span style="color: blue;">return</span> brush; </pre>
}<br />
<br />
We can now determine which colour to use for the background, so a call to the static PercentColorRanges.GetColor(percentageWhole) method, that we created earlier, will return the correct colour<i>. </i><br />
<br />
Finally we need to create the new Brush add that to our dictionary before returning the Brush :<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;">brush = <span style="color: blue;">new</span> <span style="color: #2b91af;">LinearGradientBrush</span>
{
StartPoint = <span style="color: blue;">new</span> <span style="color: #2b91af;">Point</span>(0, 0),
EndPoint = <span style="color: blue;">new</span> <span style="color: #2b91af;">Point</span>(1, 0),
GradientStops = <span style="color: blue;">new</span> <span style="color: #2b91af;">GradientStopCollection</span>(<span style="color: blue;">new</span>[]
{
CreateFrozenGradient(color, percentageValue),
CreateFrozenGradient(<span style="color: #2b91af;">Colors</span>.Transparent, percentageValue)
})
}</pre>
<div>
I've created a helper function <i>CreateFrozenGradient</i> which creates a <i><b>Frozen</b></i> <i>GradientStop</i> from a colour and gradient stop offset location.<br />
<br />
The Gradient stop offset is expressed using the default coordinate system - relative to the bounded item, so I can use our calculated % value. See the MappingMode property for more info.<br />
<br />
That's it - pretty simple. The sample source code can be found here.
</div>
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2116990&authkey=ABxnVoPu09xXaHM" width="98"></iframe>Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-70300798345878523852015-01-23T07:43:00.000-08:002015-03-04T05:08:23.682-08:00Detecting Binding Errors - WPF PresentationTraceSources.DataBindingSourceBy default, Visual Studio (v2012 onwards) is configured to automatically log any binding errors detected whilst running a WPF application.<br />
<div>
<br /></div>
<div>
You can see examples of such in the Debug Output window:</div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">System.Windows.Data Error: 40 : BindingExpression path error: 'MyUnknownProperty' property not found on 'object' ''Product' (HashCode=30581329)'. BindingExpression:Path=MyUnknownProperty; DataItem='Product' (HashCode=30581329); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')</span></blockquote>
<div>
This setting can be adjusted using the Debugging, Output Window settings: </div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKUGCS000sIhACp0fCVcGs4GdA7v8qqZMgZXMr5CD23MxHtocGFgF3VCNnXev4HjL_QP1nkLDkrbHOcKZWuaVlBPyT-lNnyHz-qtpfkpAICrYfGEUX0Qy4mLlnuVqPC3xhOd3M/s1600/wpfoutput.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKUGCS000sIhACp0fCVcGs4GdA7v8qqZMgZXMr5CD23MxHtocGFgF3VCNnXev4HjL_QP1nkLDkrbHOcKZWuaVlBPyT-lNnyHz-qtpfkpAICrYfGEUX0Qy4mLlnuVqPC3xhOd3M/s1600/wpfoutput.jpg" height="317" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
Often, it's useful to be notified in a <b><i>more intrusive manner </i></b>when a binding error has been detected...rather than silently continuing, just as WPF does.</div>
<div>
<br /></div>
<div>
In order to add your own handling, you need to add a class that derives from <span style="background-color: white; color: #2b91af; font-family: Consolas; font-size: 13px;">TraceListener</span> to the <span style="background-color: white; color: #2b91af; font-family: Consolas; font-size: 13px;">PresentationTraceSources</span><span style="background-color: white; font-family: Consolas; font-size: 13px;">.DataBindingSource.Listeners </span>collection.</div>
<div>
<br />
<a name='more'></a><br /></div>
<div>
Your TraceListener needs to implement the following abstract methods:</div>
<div>
<br /></div>
<div>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">public</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> WriteLine(<span style="color: blue;">string</span> message)
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">public</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> Write(<span style="color: blue;">string</span> message)
</pre>
</pre>
</div>
<div>
<br /></div>
<div>
Generally, you'll only add implementation details to the <span style="background-color: white; font-family: Consolas; font-size: 13px;">WriteLine</span> method. When a binding error is detected the WPF will call your WriteLine and Write methods.<br />
<br />
<b>Full Source:</b><br />
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2111324&authkey=AJ59D7C-YM6f2ms" width="98"></iframe>
</div>
<div>
<br /></div>
<h2>
Listing</h2>
<h2>
<span style="background-color: white; color: blue; font-family: Consolas; font-size: 13px;">internal</span><span style="background-color: white; font-family: Consolas; font-size: 13px;"> </span><span style="background-color: white; color: blue; font-family: Consolas; font-size: 13px;">class</span><span style="background-color: white; font-family: Consolas; font-size: 13px;"> </span><span style="background-color: white; color: #2b91af; font-family: Consolas; font-size: 13px;">BindingListener</span><span style="background-color: white; font-family: Consolas; font-size: 13px;"> : </span><span style="background-color: white; font-family: Consolas; font-size: 13px;"><span style="color: #2b91af;">TraceListener</span></span><span style="background-color: white; font-family: Consolas; font-size: 13px;">{</span></h2>
<div>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"> <span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">BindingListener</span> Current = <span style="color: blue;">new</span> <span style="color: #2b91af;">BindingListener</span>();
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> Uninitialised = 0;
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> Initialised = 1;
<span style="color: blue;">private</span> <span style="color: blue;">int</span> _initialised = Uninitialised;
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Initialise()
{
<span style="color: blue;">if</span> (<span style="color: #2b91af;">Interlocked</span>.CompareExchange(<span style="color: blue;">ref</span> _initialised, Initialised, Uninitialised) == Uninitialised)
{
<span style="color: #2b91af;">PresentationTraceSources</span>.DataBindingSource.Listeners.Add(<span style="color: blue;">this</span>);
}
}
<span style="color: blue;">public</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> WriteLine(<span style="color: blue;">string</span> message)
{
<span style="color: blue;">throw</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">ApplicationException</span>(message);
}
<span style="color: blue;">public</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> Write(<span style="color: blue;">string</span> message)
{}
}</pre>
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
In the above example we wrap access to the BindingListener with a static Current property. When Initialise() is called, provided it's the first time is has been called, then the class is added to<span style="font-family: 'Times New Roman'; font-size: small;"> </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">PresentationTraceSources</span><span style="background-color: white; font-family: Consolas; font-size: 13px;">.DataBindingSource.Listeners</span><span style="font-family: 'Times New Roman'; font-size: small;"> </span><br />
<span style="font-family: 'Times New Roman'; font-size: small;"><br /></span>
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
It's probably overkill using Interlocked.CompareExchange for thread-safety as generally you'll be calling Initialise() from the main GUI thread, as your app starts up:<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">public</span> <span style="color: blue;">partial</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">App</span> : <span style="color: #2b91af;">Application</span>
{
<span style="color: blue;">protected</span> <span style="color: blue;">override</span> <span style="color: blue;">void</span> OnStartup(<span style="color: #2b91af;">StartupEventArgs</span> e)
{
<span style="color: #2b91af;">BindingListener</span>.Current.Initialise();
<span style="color: blue;">base</span>.OnStartup(e);
}
}</pre>
<span style="font-family: Times New Roman;"><span style="white-space: normal;">When you run the application (in debug mode) and there is any form of binding error, you'll see following exception dialog:</span></span> <pre style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<div class="separator" style="background-color: white; clear: both; font-family: Consolas; font-size: 13px; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdmKUZuwYdPQ7AHJq4GPMkBBVBJDexVl9ZSH55Oq0zZQJwMH8FhaeevgWpWTaM-CwoLMNGd9NI1i4TyBa1QnaA0KvA89oKX-aFWtGpEgyd-kJbFURRd1Jl9iIfbz4G4ckOpvDi/s1600/bindingError.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdmKUZuwYdPQ7AHJq4GPMkBBVBJDexVl9ZSH55Oq0zZQJwMH8FhaeevgWpWTaM-CwoLMNGd9NI1i4TyBa1QnaA0KvA89oKX-aFWtGpEgyd-kJbFURRd1Jl9iIfbz4G4ckOpvDi/s1600/bindingError.jpg" height="240" width="400" /></a></div>
<div class="separator" style="background-color: white; clear: both; font-family: Consolas; font-size: 13px; text-align: center;">
</div>
<div>
This can be quite annoying, but at least you won't miss any binding errors.
</div>
</pre>
</div>
One thing to note: your listener only gets called in Debug builds!
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-36022649523370913672015-01-21T10:11:00.000-08:002015-03-04T05:08:45.901-08:00WPF Frame Rate CalculatorI've been playing around with WPF's <span style="background-color: white; color: #2b91af; font-family: Consolas; font-size: 13px;">CompositionTarget</span><span style="background-color: white; font-family: Consolas; font-size: 13px;">.Rendering </span>event<span style="background-color: white; font-family: Consolas; font-size: 13px;">.</span><br />
From the documentation:<br />
<br />
<blockquote class="tr_bq">
<i><b>CompositionTarget.Rendering </b></i><i>Occurs just before the objects in the composition tree are rendered.</i><i>The Rendering event is routed to the specified event handler after animation and layout have been applied to the composition tree.</i></blockquote>
From this event, I've been looked at a way to calculate how many frames are being rendered per second in a WPF application.<br />
<br />
Caveat Emptor:<br />
<ol>
<li>CompositionTarget.Rendering is a static member, so remember to unhook your delegate when you don't need it...leaks....</li>
<li>The act of adding a delegate to CompositionTarget.Rendering <i>might</i> slow your WPF application down...so it's probably best used debugging performance, or as a user configurable options when used in a production environment. </li>
<li>My calculations aren't very scientific..but the numbers produced are close to those seen by attaching to the application with <b>Perforator</b> from WPF Performance Suite.</li>
</ol>
<div>
This could be handy if a low frame rate is detected, you could log the time stamp (using something like log4net) and compare what was going on in the rest of you app.</div>
<div>
<br />
<a name='more'></a><br /><br />
<b>Links:</b></div>
<div>
<ul>
<li><a href="https://msdn.microsoft.com/en-us/library/aa969767(v=vs.110).aspx" style="font-family: inherit;" target="_blank">WPF Performance Suite</a><span style="font-family: inherit;"> </span></li>
<li><span style="background-color: white;">If you're struggling to find the .MSI, the 64 bit install can be found </span><a href="http://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceToolKit_amd64/wpt_x64.msi" style="background-color: white;" target="_blank">here</a><span style="background-color: white;">.</span></li>
</ul>
</div>
From the WPF Peformance Suite, frame rate:<br />
<blockquote class="tr_bq" style="background: white;">
<i style="font-family: 'Times New Roman'; white-space: normal;">For applications without animation, this value should be near 0. During animations in a well-performing application, Frame Rate should be close to the monitor’s refresh rate (typically 60 or 75).</i><span style="background-color: transparent;"> </span></blockquote>
<pre style="background: white;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
WPF applications start off with two main threads; one hidden background thread for rendering and one to manage the user interface.<br />
<pre style="background: white;"><span style="background-color: transparent; white-space: normal;"><span style="font-family: Times New Roman;">
</span></span></pre>
<pre style="background: white;"><span style="background-color: transparent; white-space: normal;"><span style="font-family: Times New Roman;">
</span></span></pre>
I've created a FrameRateCalculator class that hooks into CompositionTarget.Rendering and calculates the current rate, once per second. Using Reactive Extensions there is an Observable property which I subscribe to in order to receive frame rate updates. You'll need to run the following command into NuGet package manager console in Visual Studio:<br />
<pre style="background: white;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background: white;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background: white;"><blockquote class="tr_bq">
Install-Package Rx-PlatformServices</blockquote>
</pre>
FrameRateCalculator uses a couple of basic multithreading practices to ensure data is accurate (NB these could have been improved further - but for now I'll stick with basic CompareAndSwap operations).<br />
<br />
<b>Full Source:</b><br />
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2111089&authkey=ABtaSS9HTgQkcoo" width="98"></iframe>
<b><br /></b>
<br />
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The frame rate's details are wrapped into single class (struct..for next version):</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">class</span> <span style="color: #2b91af;">FrameRate</span>
{
<span style="color: blue;">public</span> FrameRate(<span style="color: blue;">string</span> time, <span style="color: blue;">int</span> frames)
{
Time = time;
Frames = frames;
}
<span style="color: blue;">public</span> <span style="color: blue;">string</span> Time { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: blue;">int</span> Frames { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">A string has been used to identify the <i>Time </i>as a convenience for the WPF graphing tool that I've used for the demo - ideally this could be a TimeSpan - as I actually use DateTime.Now to determine the 'time'. </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The guts of </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">FrameRateCalculator</span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">:</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Diagnostics;
<span style="color: blue;">using</span> System.Reactive.Linq;
<span style="color: blue;">using</span> System.Reactive.Subjects;
<span style="color: blue;">using</span> System.Threading;
<span style="color: blue;">using</span> System.Windows.Media;</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">class</span> <span style="color: #2b91af;">FrameRateCalculator</span> : <span style="color: #2b91af;">IDisposable</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">ReaderWriterLockSlim</span> _lock = <span style="color: blue;">new</span> <span style="color: #2b91af;">ReaderWriterLockSlim</span>();
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">ISubject</span><<span style="color: #2b91af;">FrameRate</span>> _rateSubject = <span style="color: blue;">new</span> <span style="color: #2b91af;">Subject</span><<span style="color: #2b91af;">FrameRate</span>>();
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Stopwatch</span> _frameRateCalcStopWatch = <span style="color: blue;">new</span> <span style="color: #2b91af;">Stopwatch</span>();
<span style="color: blue;">private</span> <span style="color: blue;">int</span> _frameCount;
<span style="color: blue;">private</span> <span style="color: blue;">int</span> _previousFrameCount;
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Timer</span> _frameRateCalcTimer;
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> IsNotRunning= 0;
<span style="color: blue;">private</span> <span style="color: blue;">const</span> <span style="color: blue;">int</span> IsRunning = 1;
<span style="color: blue;">private</span> <span style="color: blue;">int</span> _isRunning = IsNotRunning;
<span style="color: blue;">public</span> FrameRateCalculator()
{
_frameRateCalcTimer = <span style="color: blue;">new</span> <span style="color: #2b91af;">Timer</span>(CalculateFrameRate, <span style="color: blue;">null</span>, <span style="color: #2b91af;">TimeSpan</span>.Zero, <span style="color: #2b91af;">TimeSpan</span>.Zero);
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Start()
{
<span style="color: blue;">if</span> (<span style="color: #2b91af;">Interlocked</span>.CompareExchange(<span style="color: blue;">ref</span> _isRunning, IsRunning, IsNotRunning) == IsNotRunning)
{
_frameRateCalcStopWatch.Start();
_frameRateCalcTimer.Change(<span style="color: #2b91af;">TimeSpan</span>.Zero, <span style="color: #2b91af;">TimeSpan</span>.FromSeconds(1));
<span style="color: #2b91af;">CompositionTarget</span>.Rendering += CompositionTargetRendering;
}
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> CompositionTargetRendering(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">EventArgs</span> e)
{
_lock.EnterWriteLock();
<span style="color: blue;">try</span>
{
_frameCount++;
}
<span style="color: blue;">finally</span>
{
_lock.ExitWriteLock();
}
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Stop()
{
StopOrReset(<span style="color: blue;">true</span>);
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Reset()
{
StopOrReset(<span style="color: blue;">false</span>);
}
<span style="color: blue;">public</span> <span style="color: #2b91af;">IObservable</span><<span style="color: #2b91af;">FrameRate</span>> Observable
{
<span style="color: blue;">get</span> { <span style="color: blue;">return</span> _rateSubject.AsObservable(); }
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> StopOrReset(<span style="color: blue;">bool</span> stop)
{
<span style="color: blue;">if</span> (<span style="color: #2b91af;">Interlocked</span>.CompareExchange(<span style="color: blue;">ref</span> _isRunning, IsNotRunning, IsRunning) == IsRunning)
{
_frameRateCalcTimer.Change(<span style="color: #2b91af;">TimeSpan</span>.Zero, <span style="color: #2b91af;">TimeSpan</span>.Zero);
<span style="color: blue;">if</span> (stop)
_frameRateCalcStopWatch.Stop();
<span style="color: blue;">else</span>
_frameRateCalcStopWatch.Reset();
<span style="color: #2b91af;">CompositionTarget</span>.Rendering -= CompositionTargetRendering;
}
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> CalculateFrameRate(<span style="color: blue;">object</span> state)
{
<span style="color: blue;">int</span> framesThisTick;
_lock.EnterReadLock();
<span style="color: blue;">try</span>
{
<span style="color: blue;">var</span> tempFrameCount = _frameCount;
framesThisTick = (_frameCount - _previousFrameCount);
_previousFrameCount = tempFrameCount;
}
<span style="color: blue;">finally</span>
{
_lock.ExitReadLock();
}
<span style="color: blue;">var</span> tickTime = _frameRateCalcStopWatch.Elapsed.ToString(<span style="color: #a31515;">@"mm\:ss"</span>);
<span style="color: blue;">var</span> frameRate = <span style="color: blue;">new</span> <span style="color: #2b91af;">FrameRate</span>(tickTime, framesThisTick);
_rateSubject.OnNext(frameRate);
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Dispose()
{
_frameRateCalcTimer.Dispose();
}
}</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The constructor sets up a Timer (from System.Threading namespace). The callback </span><span style="font-family: Consolas; font-size: 13px;">CalculateFrameRate </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">gets </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;"> called by a ThreadPool thread so needs to be re-entrant..although this re-entrancy is unlikely to be a problem as it's called only once each second. </span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">I dont want to use a WPF thread as that would skew my calculations.</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The </span><span style="font-family: Consolas; font-size: 13px;">Start() </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">method uses </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">Interlocked</span><span style="font-family: Consolas; font-size: 13px;">.CompareExchange</span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;"> to ensure that we're not adding multiple delegates to </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">CompositionTarget</span><span style="font-family: Consolas; font-size: 13px;">.Rendering </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">- still will start the process of lsitneign for render calls, calculating the current frame rate and signal anyone listening via </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">IObservable</span><span style="font-family: Consolas; font-size: 13px;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">FrameRate</span><span style="font-family: Consolas; font-size: 13px;">> Observable.</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">Start() </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;"> adds the delegate </span><span style="font-family: Consolas; font-size: 13px;">CompositionTargetRendering:</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: #2b91af;">CompositionTarget</span>.Rendering += CompositionTargetRendering;</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">void</span> CompositionTargetRendering(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">EventArgs</span> e)</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">{
_lock.EnterWriteLock();
<span style="color: blue;">try</span>
{
_frameCount++;
}
<span style="color: blue;">finally</span>
{
_lock.ExitWriteLock();
}
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">Each time WPF tells us that a rendering operation has occurred, we increment the frameCount value.</span></span></span></pre>
</pre>
</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The frame rate calculation code is found in </span><span style="font-family: Consolas; font-size: 13px;">CalculateFrameRate(</span><span style="color: blue; font-family: Consolas; font-size: 13px;">object</span><span style="font-family: Consolas; font-size: 13px;"> state):</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">void</span> CalculateFrameRate(<span style="color: blue;">object</span> state)
{
<span style="color: blue;">int</span> framesThisTick;
_lock.EnterReadLock();
<span style="color: blue;">try</span>
{
<span style="color: blue;">var</span> tempFrameCount = _frameCount;
framesThisTick = (_frameCount - _previousFrameCount);
_previousFrameCount = tempFrameCount;
}
<span style="color: blue;">finally</span>
{
_lock.ExitReadLock();
}
<span style="color: blue;">var</span> tickTime = _frameRateCalcStopWatch.Elapsed.ToString(<span style="color: #a31515;">@"mm\:ss"</span>);
<span style="color: blue;">var</span> frameRate = <span style="color: blue;">new</span> <span style="color: #2b91af;">FrameRate</span>(tickTime, framesThisTick);
_rateSubject.OnNext(frameRate);
}</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">It's pretty simple, using a </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">ReaderWriterLockSlim </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">lock to enable thread safe access, we determine the number of frames that have been rendered in the last second.</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The </span><span style="font-family: Consolas; font-size: 13px;">Stop() </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">method safely stops the frame rate calculations.</span></pre>
</pre>
</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">To use the calculator, you'll need to call Start() and subscribe to the observable sequence like this:</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">var</span> frameRates = <span style="color: blue;">new</span> <span style="color: #2b91af;">ObservableCollection</span><<span style="color: #2b91af;">FrameRate</span>>();</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">var</span> calculator = <span style="color: blue;">new</span> <span style="color: #2b91af;">FrameRateCalculator</span>();</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;">calculator.Start()</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">var </span>frameSubscription = calculator.Observable
.ObserveOn(<span style="color: #2b91af;">SynchronizationContext</span>.Current)
.Subscribe(fr =>
{
frameRates.Add(fr);
});</pre>
</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">I'm storing each of the FrameRate items received in an </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">ObservableCollection</span><span style="font-family: Consolas; font-size: 13px;"><</span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">FrameRate</span><span style="font-family: Consolas; font-size: 13px;">> </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">so I need to use RX's ObserveOn() extension</span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;"> method to ensure the WPF Dispatcher thread adds to the ObservableCollection.</span></pre>
</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">To demo this in WPF, I've created a ViewModel </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">FrameRateViewModel </span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">which I'll bind to in a moment</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Collections.ObjectModel;
<span style="color: blue;">using</span> System.ComponentModel;
<span style="color: blue;">using</span> System.Reactive.Linq;
<span style="color: blue;">using</span> System.Runtime.CompilerServices;
<span style="color: blue;">using</span> System.Threading;
<span style="color: blue;">using</span> System.Windows.Input;
<span style="color: blue;">class</span> <span style="color: #2b91af;">FrameRateViewModel</span> : <span style="color: #2b91af;">INotifyPropertyChanged</span>, <span style="color: #2b91af;">IDisposable</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">FrameRateCalculator</span> _calculator = <span style="color: blue;">new</span> <span style="color: #2b91af;">FrameRateCalculator</span>();
<span style="color: blue;">private</span> <span style="color: #2b91af;">IDisposable</span> _frameSubscription;
<span style="color: blue;">public</span> FrameRateViewModel()
{
FrameRates = <span style="color: blue;">new</span> <span style="color: #2b91af;">ObservableCollection</span><<span style="color: #2b91af;">FrameRate</span>>();
StartCommand = <span style="color: blue;">new</span> <span style="color: #2b91af;">RelayCommand</span>(_ => _calculator.Start());
StopCommand = <span style="color: blue;">new</span> <span style="color: #2b91af;">RelayCommand</span>(_ => _calculator.Stop());
ResetCommand = <span style="color: blue;">new</span> <span style="color: #2b91af;">RelayCommand</span>(_ =>
{
FrameRates.Clear();
_calculator.Reset();
});
_frameSubscription = _calculator.Observable
.ObserveOn(<span style="color: #2b91af;">SynchronizationContext</span>.Current)
.Subscribe(fr =>
{
FrameRates.Add(fr);
<span style="color: blue;">if</span> (FrameRates.Count > 30)
{
FrameRates.RemoveAt(0);
}
CurrentFrameRate = fr.Frames;
});
}
<span style="color: blue;">public</span> <span style="color: #2b91af;">ObservableCollection</span><<span style="color: #2b91af;">FrameRate</span>> FrameRates { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">private</span> <span style="color: blue;">int</span> _currentFrameRate;
<span style="color: blue;">public</span> <span style="color: blue;">int</span> CurrentFrameRate
{
<span style="color: blue;">get</span> { <span style="color: blue;">return</span> _currentFrameRate; }
<span style="color: blue;">set</span>
{
<span style="color: blue;">if</span> (_currentFrameRate != <span style="color: blue;">value</span>)
{
_currentFrameRate = <span style="color: blue;">value</span>;
NotifyPropertyChanged();
}
}
}
<span style="color: blue;">public</span> <span style="color: #2b91af;">ICommand</span> StopCommand { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: #2b91af;">ICommand</span> StartCommand { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: #2b91af;">ICommand</span> ResetCommand { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: blue;">event</span> <span style="color: #2b91af;">PropertyChangedEventHandler</span> PropertyChanged;
<span style="color: blue;">private</span> <span style="color: blue;">void</span> NotifyPropertyChanged([<span style="color: #2b91af;">CallerMemberName</span>] <span style="color: blue;">string</span> propertyName = <span style="color: blue;">null</span>)
{
<span style="color: blue;">var</span> handler = PropertyChanged;
<span style="color: blue;">if</span> (handler != <span style="color: blue;">null</span>) handler(<span style="color: blue;">this</span>, <span style="color: blue;">new</span> <span style="color: #2b91af;">PropertyChangedEventArgs</span>(propertyName));
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Dispose()
{
_frameSubscription.Dispose();
_calculator.Dispose();
}
}</pre>
</pre>
</pre>
<span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">The observable subscription ensures that we have only a maximum of 30 ticks (for the last 30 seconds)...this make the demo screen's chart look <i>similar</i> to that in Perforator:</span></pre>
<pre style="background: white;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">_frameSubscription = _calculator.Observable
.ObserveOn(<span style="color: #2b91af;">SynchronizationContext</span>.Current)
.Subscribe(fr =>
{
FrameRates.Add(fr);
<span style="color: blue;">if</span> (FrameRates.Count > 30)
{
FrameRates.RemoveAt(0);
}
CurrentFrameRate = fr.Frames;
});</pre>
</pre>
<pre style="background: white;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">
</span></pre>
<pre style="background: white;"><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;">I'm using</span><span style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal;"> MVVM practices here, so Stop, Start and Reset commands are based on the now common </span> <span style="color: #2b91af; font-family: Consolas; font-size: 13px;">RelayCommand:</span></pre>
<pre style="background: white;"><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">
</span></pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">RelayCommand</span> : <span style="color: #2b91af;">ICommand</span>
{
<span style="color: blue;">readonly</span> <span style="color: #2b91af;">Action</span><<span style="color: blue;">object</span>> _execute;
<span style="color: blue;">readonly</span> <span style="color: #2b91af;">Predicate</span><<span style="color: blue;">object</span>> _canExecute;
<span style="color: blue;">public</span> RelayCommand(<span style="color: #2b91af;">Action</span><<span style="color: blue;">object</span>> execute)
: <span style="color: blue;">this</span>(execute, <span style="color: blue;">null</span>)
{}
<span style="color: blue;">public</span> RelayCommand(<span style="color: #2b91af;">Action</span><<span style="color: blue;">object</span>> execute, <span style="color: #2b91af;">Predicate</span><<span style="color: blue;">object</span>> canExecute)
{
<span style="color: blue;">if</span> (execute == <span style="color: blue;">null</span>)
<span style="color: blue;">throw</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">ArgumentNullException</span>(<span style="color: #a31515;">"execute"</span>);
_execute = execute;
_canExecute = canExecute;
}
<span style="color: blue;">public</span> <span style="color: blue;">bool</span> CanExecute(<span style="color: blue;">object</span> parameter)
{
<span style="color: blue;">return</span> _canExecute == <span style="color: blue;">null</span> || _canExecute(parameter);
}
<span style="color: blue;">public</span> <span style="color: blue;">event</span> <span style="color: #2b91af;">EventHandler</span> CanExecuteChanged
{
<span style="color: blue;">add</span> { <span style="color: #2b91af;">CommandManager</span>.RequerySuggested += <span style="color: blue;">value</span>; }
<span style="color: blue;">remove</span> { <span style="color: #2b91af;">CommandManager</span>.RequerySuggested -= <span style="color: blue;">value</span>; }
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Execute(<span style="color: blue;">object</span> parameter)
{
_execute(parameter);
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;">}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">My WPF demo app has a single WPF Window which uses a Chart control from the WPF Toolkit. You'll need to install the WPF Tool kit from <a href="http://wpf.codeplex.com/releases/view/40535" target="_blank">here</a> </span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">from if you want to use the following XAML.</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">NB: I'm mixing WPF data binding with a little bit code behind. I wouldn't normally use code behind...but it's only a demo ;)</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">1. Add a new window called </span><span style="font-family: Times New Roman;"><span style="white-space: normal;">MainWindow.xaml:</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;"><</span><span style="color: #a31515;">Window</span><span style="color: red;"> x</span><span style="color: blue;">:</span><span style="color: red;">Class</span><span style="color: blue;">=</span><span style="color: blue;">"BlogFrameRate.MainWindow"</span>
<span style="color: red;"> xmlns</span><span style="color: blue;">=</span><span style="color: blue;">"http://schemas.microsoft.com/winfx/2006/xaml/presentation"</span>
<span style="color: red;"> xmlns</span><span style="color: blue;">:</span><span style="color: red;">x</span><span style="color: blue;">=</span><span style="color: blue;">"http://schemas.microsoft.com/winfx/2006/xaml"</span>
<span style="color: red;"> xmlns</span><span style="color: blue;">:</span><span style="color: red;">chartToolkit</span><span style="color: blue;">=</span><span style="color: blue;">"clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"</span>
<span style="color: red;"> Title</span><span style="color: blue;">=</span><span style="color: blue;">"MainWindow"</span><span style="color: red;"> Height</span><span style="color: blue;">=</span><span style="color: blue;">"700"</span><span style="color: red;"> Width</span><span style="color: blue;">=</span><span style="color: blue;">"700"</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Grid</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Grid.RowDefinitions</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">RowDefinition</span><span style="color: red;"> Height</span><span style="color: blue;">=</span><span style="color: blue;">"Auto"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">RowDefinition</span><span style="color: red;"> Height</span><span style="color: blue;">=</span><span style="color: blue;">"Auto"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">RowDefinition</span><span style="color: red;"> Height</span><span style="color: blue;">=</span><span style="color: blue;">"*"</span><span style="color: blue;">/></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Grid.RowDefinitions</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Grid.ColumnDefinitions</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">ColumnDefinition</span><span style="color: red;"> Width</span><span style="color: blue;">=</span><span style="color: blue;">"*"</span><span style="color: blue;"> /></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Grid.ColumnDefinitions</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">StackPanel</span><span style="color: red;"> Grid.Column</span><span style="color: blue;">=</span><span style="color: blue;">"0"</span><span style="color: red;"> Grid.Row</span><span style="color: blue;">=</span><span style="color: blue;">"0"</span><span style="color: red;"> Orientation</span><span style="color: blue;">=</span><span style="color: blue;">"Horizontal"</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Button</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Add One"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"8"</span><span style="color: red;"> Click</span><span style="color: blue;">=</span><span style="color: blue;">"Button_Click"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Button</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Add Many"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"2,8"</span><span style="color: red;"> Click</span><span style="color: blue;">=</span><span style="color: blue;">"AddManyClick"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Label</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Items:"</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"2,8"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Label</span><span style="color: red;"> Name</span><span style="color: blue;">=</span><span style="color: blue;">"ItemCountLabel"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"-4,8,4,8"</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span> <span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">CheckBox</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Delay"</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"8"</span><span style="color: red;"> Name</span><span style="color: blue;">=</span><span style="color: blue;">"CheckBoxDelay"</span>
<span style="color: red;"> Click</span><span style="color: blue;">=</span><span style="color: blue;">"CheckBoxDelay_Click"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Slider</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: red;"> Width</span><span style="color: blue;">=</span><span style="color: blue;">"100"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"-4,8"</span><span style="color: red;"> Name</span><span style="color: blue;">=</span><span style="color: blue;">"Slider"</span>
<span style="color: red;"> AutoToolTipPlacement</span><span style="color: blue;">=</span><span style="color: blue;">"BottomRight"</span>
<span style="color: red;"> ValueChanged</span><span style="color: blue;">=</span><span style="color: blue;">"Slider_ValueChanged"</span><span style="color: red;"> SmallChange</span><span style="color: blue;">=</span><span style="color: blue;">"0.01"</span><span style="color: red;"> TickFrequency</span><span style="color: blue;">=</span><span style="color: blue;">"100"</span><span style="color: red;"> Maximum</span><span style="color: blue;">=</span><span style="color: blue;">"100"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Label</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Frame Rate:"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"8"</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Label</span><span style="color: red;"> Content</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> CurrentFrameRate</span><span style="color: blue;">,</span><span style="color: red;"> FallbackValue</span><span style="color: blue;">=</span><span style="color: blue;">na</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: red;"> VerticalAlignment</span><span style="color: blue;">=</span><span style="color: blue;">"Center"</span><span style="color: red;"> MinWidth</span><span style="color: blue;">=</span><span style="color: blue;">"20"</span><span style="color: blue;"> /></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Button</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Start"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"4,8"</span><span style="color: red;"> Command</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> StartCommand</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Button</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Stop"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"4,8"</span><span style="color: red;"> Command</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> StopCommand</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: blue;">/></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Button</span><span style="color: red;"> Content</span><span style="color: blue;">=</span><span style="color: blue;">"Reset"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: red;"> Margin</span><span style="color: blue;">=</span><span style="color: blue;">"4, 8"</span><span style="color: red;"> Command</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> ResetCommand</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: blue;">/></span>
<span style="color: blue;"></</span><span style="color: #a31515;">StackPanel</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">Chart</span><span style="color: red;"> Grid.Column</span><span style="color: blue;">=</span><span style="color: blue;">"0"</span><span style="color: red;"> Grid.Row</span><span style="color: blue;">=</span><span style="color: blue;">"1"</span><span style="color: red;"> Padding</span><span style="color: blue;">=</span><span style="color: blue;">"4"</span><span style="color: blue;"> ></span>
<span style="color: blue;"><</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">Chart.LegendStyle</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Style</span><span style="color: red;"> TargetType</span><span style="color: blue;">=</span><span style="color: blue;">"Control"</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">Setter</span><span style="color: red;"> Property</span><span style="color: blue;">=</span><span style="color: blue;">"Width"</span><span style="color: red;"> Value</span><span style="color: blue;">=</span><span style="color: blue;">"0"</span><span style="color: blue;">/></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Style</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">Chart.LegendStyle</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">AreaSeries</span><span style="color: red;"> DependentValuePath</span><span style="color: blue;">=</span><span style="color: blue;">"Frames"</span><span style="color: red;"> IndependentValuePath</span><span style="color: blue;">=</span><span style="color: blue;">"Time"</span>
<span style="color: red;"> ItemsSource</span><span style="color: blue;">="{</span><span style="color: #a31515;">Binding</span><span style="color: red;"> FrameRates</span><span style="color: blue;">}</span><span style="color: blue;">"</span><span style="color: blue;"> ></span>
<span style="color: blue;"><</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">AreaSeries.IndependentAxis</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">CategoryAxis</span><span style="color: red;"> Orientation</span><span style="color: blue;">=</span><span style="color: blue;">"X"</span> <span style="color: blue;"> /></span>
<span style="color: blue;"></</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">AreaSeries.IndependentAxis</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">AreaSeries</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">chartToolkit</span><span style="color: blue;">:</span><span style="color: #a31515;">Chart</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">ScrollViewer</span><span style="color: red;"> VerticalScrollBarVisibility</span><span style="color: blue;">=</span><span style="color: blue;">"Auto"</span><span style="color: red;"> Grid.Column</span><span style="color: blue;">=</span><span style="color: blue;">"0"</span><span style="color: red;"> Grid.Row</span><span style="color: blue;">=</span><span style="color: blue;">"2"</span><span style="color: blue;">></span>
<span style="color: blue;"><</span><span style="color: #a31515;">WrapPanel</span> <span style="color: red;"> Orientation</span><span style="color: blue;">=</span><span style="color: blue;">"Horizontal"</span><span style="color: red;"> Name</span><span style="color: blue;">=</span><span style="color: blue;">"ImagePanel"</span><span style="color: blue;"> /></span>
<span style="color: blue;"></</span><span style="color: #a31515;">ScrollViewer</span><span style="color: blue;"> ></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Grid</span><span style="color: blue;">></span>
<span style="color: blue;"></</span><span style="color: #a31515;">Window</span><span style="color: blue;">></span></pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">2. Open the </span><span style="font-family: Times New Roman;"><span style="white-space: normal;">MainWindow.xaml.cs file and replace with the following code. The Add buttons add images to the image panel. Each image uses a </span></span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">DoubleAnimationUsingKeyFrames</span><span style="font-family: Consolas; font-size: 13px;"> </span><span style="font-family: 'Times New Roman'; white-space: normal;">to rotate the image (the spins slow down when you use the GUI delay code, described later):</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Windows;
<span style="color: blue;">using</span> System.Windows.Controls;
<span style="color: blue;">using</span> System.Windows.Media;
<span style="color: blue;">using</span> System.Windows.Media.Animation;
<span style="color: blue;">using</span> System.Windows.Media.Imaging;
<span style="color: blue;">namespace</span> BlogFrameRate
{
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"><summary></span>
<span style="color: grey;">///</span><span style="color: green;"> Interaction logic for MainWindow.xaml</span>
<span style="color: grey;">///</span><span style="color: green;"> </span><span style="color: grey;"></summary></span>
<span style="color: blue;">public</span> <span style="color: blue;">partial</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">MainWindow</span> : <span style="color: #2b91af;">Window</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">FrameRateViewModel</span> _viewModel = <span style="color: blue;">new</span> <span style="color: #2b91af;">FrameRateViewModel</span>();
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">GuiDelay</span> _delayer;
<span style="color: blue;">public</span> MainWindow()
{
InitializeComponent();
_delayer = <span style="color: blue;">new</span> <span style="color: #2b91af;">GuiDelay</span>(Dispatcher);
DataContext = _viewModel;
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> Button_Click(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">RoutedEventArgs</span> e)
{
AddImage();
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> AddImage()
{
<span style="color: blue;">var</span> bitmapImage = <span style="color: blue;">new</span> <span style="color: #2b91af;">BitmapImage</span>();
bitmapImage.BeginInit();
bitmapImage.UriSource = <span style="color: blue;">new</span> <span style="color: #2b91af;">Uri</span>(<span style="color: #a31515;">@"max.jpg"</span>, <span style="color: #2b91af;">UriKind</span>.RelativeOrAbsolute);
bitmapImage.EndInit();
<span style="color: blue;">var</span> imageControl = <span style="color: blue;">new</span> <span style="color: #2b91af;">Image</span>
{
Source = bitmapImage,
Stretch = <span style="color: #2b91af;">Stretch</span>.None,
RenderTransform = <span style="color: blue;">new</span> <span style="color: #2b91af;">RotateTransform</span>(),
RenderTransformOrigin = <span style="color: blue;">new</span> <span style="color: #2b91af;">Point</span>(0.5D, 0.5D)
};
<span style="color: green;">//var angleAnimation = CreateAnimation();</span>
<span style="color: blue;">var</span> angleAnimation = CreateAnimationKeyFrame();
imageControl.RenderTransform.BeginAnimation(<span style="color: #2b91af;">RotateTransform</span>.AngleProperty, angleAnimation);
ImagePanel.Children.Add(imageControl);
ItemCountLabel.Content = ImagePanel.Children.Count;
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">DoubleAnimation</span> CreateAnimation()
{
<span style="color: blue;">return</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">DoubleAnimation</span>
{
From = 0,
To = 360,
Duration = <span style="color: blue;">new</span> <span style="color: #2b91af;">Duration</span>(<span style="color: #2b91af;">TimeSpan</span>.FromSeconds(1)),
RepeatBehavior = <span style="color: #2b91af;">RepeatBehavior</span>.Forever
};
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">DoubleAnimationUsingKeyFrames</span> CreateAnimationKeyFrame()
{
<span style="color: blue;">var</span> angleAnimation = <span style="color: blue;">new</span> <span style="color: #2b91af;">DoubleAnimationUsingKeyFrames</span>
{
Duration = <span style="color: blue;">new</span> <span style="color: #2b91af;">Duration</span>(<span style="color: #2b91af;">TimeSpan</span>.FromSeconds(1)),
RepeatBehavior = <span style="color: #2b91af;">RepeatBehavior</span>.Forever
};
angleAnimation.KeyFrames.Add(
<span style="color: blue;">new</span> <span style="color: #2b91af;">LinearDoubleKeyFrame</span>(360, <span style="color: #2b91af;">KeyTime</span>.FromTimeSpan(<span style="color: #2b91af;">TimeSpan</span>.FromSeconds(1))) );
<span style="color: blue;">return</span> angleAnimation;
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> AddManyClick(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">RoutedEventArgs</span> e)
{
<span style="color: blue;">for</span> (<span style="color: blue;">var</span> i = 0; i < 20; i++)
{
AddImage();
}
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> Slider_ValueChanged(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">RoutedPropertyChangedEventArgs</span><<span style="color: blue;">double</span>> e)
{
_delayer.Duration = <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(e.NewValue);
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> CheckBoxDelay_Click(<span style="color: blue;">object</span> sender, <span style="color: #2b91af;">RoutedEventArgs</span> e)
{
_delayer.IsActive = CheckBoxDelay.IsChecked.GetValueOrDefault();
}
}
}</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">3. You'll need to add an image Resource called <i>max.jpg </i>- in the demo app I've included an image 91/132 pixels of my dog Max.</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">4. Finally, add this delay class. All this does is hook into the Dispatcher thread and inject a delay as set by the slider on the main window. You don't need to implement this but, but it serves as at way to slow down the actual frame rate</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Threading;
<span style="color: blue;">using</span> System.Windows.Threading;
<span style="color: blue;">namespace</span> BlogFrameRate
{
<span style="color: blue;">class</span> <span style="color: #2b91af;">GuiDelay</span> : <span style="color: #2b91af;">IDisposable</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Timer</span> _timer;
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Dispatcher</span> _dispatcher;
<span style="color: blue;">public</span> GuiDelay(<span style="color: #2b91af;">Dispatcher</span> dispatcher)
{
Duration = <span style="color: #2b91af;">TimeSpan</span>.Zero;
_dispatcher = dispatcher;
_timer = <span style="color: blue;">new</span> <span style="color: #2b91af;">Timer</span>(BlockGuiThread, <span style="color: blue;">null</span>, <span style="color: #2b91af;">TimeSpan</span>.Zero, <span style="color: #2b91af;">TimeSpan</span>.FromMilliseconds(100));
}
<span style="color: blue;">public</span> <span style="color: #2b91af;">TimeSpan</span> Duration { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }
<span style="color: blue;">public</span> <span style="color: blue;">bool</span> IsActive { <span style="color: blue;">get</span>; <span style="color: blue;">set</span>; }
<span style="color: blue;">private</span> <span style="color: blue;">bool</span> _isInDelayLoop;
<span style="color: blue;">private</span> <span style="color: blue;">void</span> BlockGuiThread(<span style="color: blue;">object</span> state)
{
<span style="color: blue;">if</span> (IsActive)
{
_dispatcher.Invoke(() =>
{
<span style="color: blue;">if</span> (_isInDelayLoop) <span style="color: blue;">return</span>;
_isInDelayLoop = <span style="color: blue;">true</span>;
<span style="color: #2b91af;">Thread</span>.Sleep(Duration);
_isInDelayLoop = <span style="color: blue;">false</span>;
});
}
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Dispose()
{
_timer.Dispose();
}
}
}</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">Run the app, create a few spinning images, and press <i>Start </i>you should see a chart of frame rates that match those that you'd see if you attach WPF Performance Suite's Perforator to the running app at the same time.</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMyWRJMqpje7J4u8sNhQ-sfZHdJ5_HZoLuN7PkTQ3h3Rrk8kIIwvtWhQc6u_CZdHXiMCIyj0rBk1B4o1PzjiDoguRzhFfLC6Z9Z-SIQoLthvw1pu_Hp-SxqcG9Irh6kyBnwVe5/s1600/screen2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMyWRJMqpje7J4u8sNhQ-sfZHdJ5_HZoLuN7PkTQ3h3Rrk8kIIwvtWhQc6u_CZdHXiMCIyj0rBk1B4o1PzjiDoguRzhFfLC6Z9Z-SIQoLthvw1pu_Hp-SxqcG9Irh6kyBnwVe5/s1600/screen2.jpg" height="287" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7-SilaQrGrevaE90hxEWyMX1E1ZccvZDCfrV1ywdAIdshwaJLTYhBALS1zYXFovMBtPK6AqBrmv0D2S_P79NGG7QYkPkEerou-0VuHEZgr4p6MNtjbYG1fJmeYvSeK64-amQk/s1600/screen1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7-SilaQrGrevaE90hxEWyMX1E1ZccvZDCfrV1ywdAIdshwaJLTYhBALS1zYXFovMBtPK6AqBrmv0D2S_P79NGG7QYkPkEerou-0VuHEZgr4p6MNtjbYG1fJmeYvSeK64-amQk/s1600/screen1.jpg" height="294" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-size: medium;"><span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
</pre>
</pre>
</pre>
</pre>
</pre>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com3tag:blogger.com,1999:blog-24402781.post-87976725527165042302015-01-18T04:07:00.002-08:002020-06-30T06:14:44.758-07:00Code Block/Scope Timing SnippetYou've got users complaining that it takes too long for a screen to open or for a certain piece of code to run. Wouldn't it be handy to wrap the execution of a key block of code with some timing statistics?<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<br />
The following TimeIt class is derived from IDisposable which makes it easy to wrap a code block (aka <i>scope</i>) with the <span style="font-family: "courier new" , "courier" , monospace;">using </span>keyword (this will ensure that if any exceptions are raised you'll still get to final end scope logged).<br />
<br />
The timing internals are hidden way - it's easily accessed via a static <span style="font-family: "courier new" , "courier" , monospace;">Log </span>method:<br />
<br />
<span style="background-color: white; color: blue; font-family: "consolas"; font-size: 13px;">using</span><span style="background-color: white; font-family: "consolas"; font-size: 13px;"> (</span><span style="background-color: white; color: #2b91af; font-family: "consolas"; font-size: 13px;">TimeIt</span><span style="background-color: white; font-family: "consolas"; font-size: 13px;">.Log(</span><span style="background-color: white; color: #a31515; font-family: "consolas"; font-size: 13px;">"Calculate cost"</span><span style="background-color: white; font-family: "consolas"; font-size: 13px;">))</span><br />
<span style="background-color: white; font-family: "consolas"; font-size: 13px;">{</span><br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"> <span style="color: #2b91af;">Debug</span>.WriteLine(<span style="color: #a31515;">"Retrieving price.."</span>);
<span style="color: blue;">var</span> price = pricingService.GetPrice();
<span style="color: blue;">var</span> cost = notionalCalculator.GetUSDValue(price);
}</pre>
<a name='more'></a>
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: "times new roman"; font-size: small; white-space: normal;">Results in the following being written to the console:</span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: "times new roman"; font-size: small; white-space: normal;"></span></pre>
<pre style="background: white;"><span style="background-color: transparent;"><span style="font-family: "courier new" , "courier" , monospace;">Start Scope [MyClass.MyMethod]: Calculate cost</span></span></pre>
<pre style="background: white;"><span style="background-color: transparent;"><span style="font-family: "courier new" , "courier" , monospace;">R</span></span><span style="background-color: transparent; font-family: "courier new" , "courier" , monospace;">etrieving price..</span></pre>
<span style="font-family: "courier new" , "courier" , monospace;"> End Scope [</span><span style="font-family: "courier new" , "courier" , monospace;">MyClass</span><span style="font-family: "courier new" , "courier" , monospace;">.</span><span style="font-family: "courier new" , "courier" , monospace;">MyMethod</span><span style="font-family: "courier new" , "courier" , monospace;">]: Calculate cost (8 ms)</span><br />
<br />
In this implementation <span style="background-color: white; font-family: "consolas"; font-size: 13px;">WriteLog </span>simply writes to the console, but ideally you'd use something like log4net to write to a log file.<br />
<br />
<h3>
Listing </h3>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Diagnostics;
<span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">TimeIt</span> : <span style="color: #2b91af;">IDisposable</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: blue;">string</span> _fullMessage;
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">Stopwatch</span> _watch;
<span style="color: blue;">private</span> TimeIt(<span style="color: blue;">string</span> message)
{
<span style="color: blue;">const</span> <span style="color: blue;">int</span> CallerFramesToSkip = 3;
_fullMessage = <span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"[</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">.</span><span style="color: mediumseagreen;">{1}</span><span style="color: #a31515;">]: </span><span style="color: mediumseagreen;">{2}</span><span style="color: #a31515;">"</span>,
GetTypeName(CallerFramesToSkip),
GetStackMethodName(CallerFramesToSkip),
message);
WriteLog(<span style="color: #a31515;">"Start Scope {0}"</span>, _fullMessage);
_watch = <span style="color: #2b91af;">Stopwatch</span>.StartNew();
}
<span style="color: blue;">public</span> <span style="color: blue;">void</span> Dispose()
{
_watch.Stop();
WriteLog(<span style="color: #a31515;">" End Scope {0} ({1})"</span>, _fullMessage, Format(_watch.Elapsed));
}
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: #2b91af;">IDisposable</span> Log(<span style="color: blue;">string</span> format, <span style="color: blue;">params</span> <span style="color: blue;">object</span>[] args)
{
<span style="color: blue;">return</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">TimeIt</span>(<span style="color: blue;">string</span>.Format(format, args));
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> WriteLog(<span style="color: blue;">string</span> message, <span style="color: blue;">params</span> <span style="color: blue;">object</span>[] args)
{
<span style="color: green;">// Ideally you'd use log4net or similar logger to write the actual</span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: green;"> // </span><span style="color: green;">message </span><span style="color: green;">prefixed with a timestamp </span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"> <span style="color: #2b91af;">Console</span>.WriteLine(message, args);</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"> }
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> Format(<span style="color: #2b91af;">TimeSpan</span> span)
{
<span style="color: blue;">double</span> part;
<span style="color: blue;">string</span> units;
<span style="color: blue;">if</span> (span.TotalHours > 1D)
{
part = span.TotalHours;
units =<span style="color: #a31515;">"hrs"</span>;
}
<span style="color: blue;">else</span> <span style="color: blue;">if</span> (span.TotalMinutes > 1D)
{
part = span.TotalMinutes;
units = <span style="color: #a31515;">"mins"</span>;
}
<span style="color: blue;">else</span> <span style="color: blue;">if</span> (span.TotalSeconds > 1D)
{
part = span.TotalSeconds;
units = <span style="color: #a31515;">"secs"</span>;
}
<span style="color: blue;">else</span>
{
<span style="color: blue;">return</span> <span style="color: blue;">string</span>.Concat(span.Milliseconds, <span style="color: #a31515;">" ms"</span>);
}
<span style="color: blue;">return</span> <span style="color: blue;">string</span>.Format(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0:N1}</span><span style="color: #a31515;"> </span><span style="color: mediumseagreen;">{1}</span><span style="color: #a31515;">"</span>, part, units);
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> GetStackMethodName(<span style="color: blue;">int</span> framesToSkip)
{
<span style="color: blue;">var</span> name = <span style="color: #a31515;">"UNKNOWN_METHOD"</span>;
<span style="color: blue;">try</span>
{
name = <span style="color: blue;">new</span> <span style="color: #2b91af;">StackTrace</span>().GetFrame(framesToSkip).GetMethod().Name;</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"> }
<span style="color: blue;">catch</span> { }
<span style="color: blue;">return</span> name;
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> GetTypeName(<span style="color: blue;">int</span> framesToSkip)
{
<span style="color: blue;">var</span> name = <span style="color: #a31515;">"UNKNOWN_METHOD"</span>;
<span style="color: blue;">try</span>
{
name = <span style="color: blue;">new</span> <span style="color: #2b91af;">StackTrace</span>().GetFrame(framesToSkip).GetMethod().DeclaringType.Name;
}
<span style="color: blue;">catch</span> { }
<span style="color: blue;">return</span> name;
}
}</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background: white; font-size: 13px;"><h3 style="font-family: 'Times New Roman'; white-space: normal;">
Tests</h3>
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><div>
<span style="font-family: "times new roman"; font-size: small; white-space: normal;">I've added couple of simple tests that don't have an assertions. Not of much value, but I like to use Resharper as a quick way to run snippets:</span></div>
<div>
<span style="font-family: "times new roman"; font-size: small; white-space: normal;"></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkt1HgvOGGqkLdMZfuY8-BgEdhQNoWd7PfdZ3FIhgoHeYeER5kaOUTOtENE7NXW34eIG4Q7GuK74ZaoFh1BbYRqDfm6SOqnlOA7Yi_qyJs4NxWiIGqmfAcSK0rh2C59En2n_iD/s1600/runtest.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="137" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkt1HgvOGGqkLdMZfuY8-BgEdhQNoWd7PfdZ3FIhgoHeYeER5kaOUTOtENE7NXW34eIG4Q7GuK74ZaoFh1BbYRqDfm6SOqnlOA7Yi_qyJs4NxWiIGqmfAcSK0rh2C59En2n_iD/s1600/runtest.jpg" width="320" /></a></div>
<div>
<span style="color: blue;">using</span> System;<br />
<span style="color: blue;">using</span> System.Threading;<br />
<span style="color: blue;">using</span> NUnit.Framework;</div>
<div>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;">[<span style="color: #2b91af;">TestFixture</span>]
<span style="color: blue;">class</span> <span style="color: #2b91af;">TimeItTests</span>
{
[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Log_Level1Method()
{
Level1Method();
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Level1Method()
{
<span style="color: blue;">using</span> (<span style="color: #2b91af;">TimeIt</span>.Log(<span style="color: #a31515;">"Busy doing level 1 stuff"</span>))
{
<span style="color: #2b91af;">Thread</span>.Sleep(1100);
}
}
[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Log_Level2Method()
{
CallNestedMethod();
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> CallNestedMethod()
{
Level2Method();
}
<span style="color: blue;">private</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Level2Method()
{
<span style="color: blue;">using</span> (<span style="color: #2b91af;">TimeIt</span>.Log(<span style="color: #a31515;">"Busy doing level 2 stuff"</span>))
{
<span style="color: #2b91af;">Thread</span>.Sleep(<span style="color: #2b91af;">TimeSpan</span>.FromMinutes(1.1));
}
}
[<span style="color: #2b91af;">Test</span>]
[<span style="color: #2b91af;">ExpectedException</span>(<span style="color: blue;">typeof</span>(<span style="color: #2b91af;">ApplicationException</span>))]
<span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Log_CalleeThrowsException()
{
<span style="color: blue;">using</span> (<span style="color: #2b91af;">TimeIt</span>.Log(<span style="color: #a31515;">"This will break"</span>))
{
<span style="color: #2b91af;">Thread</span>.Sleep(<span style="color: #2b91af;">TimeSpan</span>.FromSeconds(2));
<span style="color: blue;">throw</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">ApplicationException</span>(<span style="color: #a31515;">"Oops, something went wrong"</span>);
}
}
}<span style="font-family: "consolas";"> </span></pre>
</div>
</pre>
</div>
<div>
<h3>
Test Output:</h3>
</div>
<span style="font-family: "times new roman";">Log_CalleeThrowsException:</span><br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px;">
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Start Scope [TimeItTests.Log_CalleeThrowsException]: This will break</span></div>
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"> End Scope [TimeItTests.Log_CalleeThrowsException]: This will break (2.0 secs)<br /> </span></span></div>
<h4>
Log_Level1Method:</h4>
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Start Scope [TimeItTests.Level1Method]: Busy doing level 1 stuff</span></div>
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace;"><span style="font-size: x-small;"> End Scope [TimeItTests.Level1Method]: Busy doing level 1 stuff (1.1 secs)<br /> </span></span></div>
<h4>
Log_Level2Method:</h4>
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">Start Scope [TimeItTests.Level2Method]: Busy doing level 2 stuff</span></div>
<div style="margin: 0px;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"> End Scope [TimeItTests.Level2Method]: Busy doing level 2 stuff (1.1 mins)</span><br />
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
</div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com0tag:blogger.com,1999:blog-24402781.post-81701724797492045882015-01-14T11:26:00.001-08:002015-03-04T05:09:18.790-08:00Using RefCount To Create Automatic Disposal and Lazy ConnectionsThis is quite a common scenario, I need to create a source data provider from some type of 'expensive' service, eg a stock market price ticker, which will automatically handle the job of connecting and disconnecting as the number of active observers changes.<br />
<br />
By 'expensive' we could mean resource-wise expensive, ie amount of memory/ cpu time or financially expensive...perhaps there's a charge levied per subscription.<br />
<br />
As an example, let's say you have a trading application with the following requirements:<br />
<ul>
<li>User can open any number of trade entry screens. </li>
<li>The trade entry screen allows the user to enter a stock symbol, eg MSFT</li>
<li>Once a symbol has been entered the application should subscribe to a market data source, eg Reuters, for that symbol and display the current price</li>
<li>If multiple screens request the same symbol then only one underlying subscription should be utilised - don't go creating yet another subscription for the same symbol</li>
<li>When a screen is closed or another symbol is selected, then we should notified when there no longer any active subscribers, thus allowing us to release the underlying source subscription</li>
<li>Subsequent requests to an already subscribed symbol should immediately send back the latest price received rather than wait until a new price arrives.<a name='more'></a></li>
</ul>
<b>Full Source:</b><br />
<iframe frameborder="0" height="120" scrolling="no" src="https://onedrive.live.com/embed?cid=178C0B68A161CA32&resid=178C0B68A161CA32%2110675&authkey=AKINhMBNsLHbnF0" width="98"></iframe>
<br />
<div>
<br />
There are a few ways to break this down with the Reactive library. For this demo I'll use v2.2.5 from NuGet. Hopefully you're familiar with the <a href="http://www.nuget.org/" target="_blank">NuGet</a> package manager.<br />
<br />
After running this in Package Manager Console:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Install-Package Rx-PlatformServices</span><br />
<br />
My <i>packages.config</i> looks like this:<br />
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;"><</span><span style="color: #a31515;">packages</span><span style="color: blue;">></span>
<span style="color: blue;"> <</span><span style="color: #a31515;">package</span><span style="color: blue;"> </span><span style="color: red;">id</span><span style="color: blue;">=</span>"<span style="color: blue;">Rx-Core</span>"<span style="color: blue;"> </span><span style="color: red;">version</span><span style="color: blue;">=</span>"<span style="color: blue;">2.2.5</span>"<span style="color: blue;"> </span><span style="color: red;">targetFramework</span><span style="color: blue;">=</span>"<span style="color: blue;">net451</span>"<span style="color: blue;"> /></span>
<span style="color: blue;"> <</span><span style="color: #a31515;">package</span><span style="color: blue;"> </span><span style="color: red;">id</span><span style="color: blue;">=</span>"<span style="color: blue;">Rx-Interfaces</span>"<span style="color: blue;"> </span><span style="color: red;">version</span><span style="color: blue;">=</span>"<span style="color: blue;">2.2.5</span>"<span style="color: blue;"> </span><span style="color: red;">targetFramework</span><span style="color: blue;">=</span>"<span style="color: blue;">net451</span>"<span style="color: blue;"> /></span>
<span style="color: blue;"> <</span><span style="color: #a31515;">package</span><span style="color: blue;"> </span><span style="color: red;">id</span><span style="color: blue;">=</span>"<span style="color: blue;">Rx-PlatformServices</span>"<span style="color: blue;"> </span><span style="color: red;">version</span><span style="color: blue;">=</span>"<span style="color: blue;">2.2.5</span>"<span style="color: blue;"> </span><span style="color: red;">targetFramework</span><span style="color: blue;">=</span>"<span style="color: blue;">net451</span>"<span style="color: blue;"> /></span>
<span style="color: blue;"></</span><span style="color: #a31515;">packages</span><span style="color: blue;">></span></pre>
</pre>
<h3>
<b>Steps</b></h3>
I'm breaking this problem down into a number of steps...if you're not interested in a step-by-step guide then jump to the bottom of this post for the full solution,.<br />
<br />
<br />
<br />
<br />
I've created a class called <span style="background-color: white; color: #2b91af; font-family: Consolas; font-size: 13px;">PriceSource </span>which takes single string constructor arg:<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Reactive.Disposables;
<span style="color: blue;">using</span> System.Reactive.Linq;
<span style="color: blue;">using</span> System.Reactive.Subjects;
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">PriceSource</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: blue;">string</span> _symbol;
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">ISubject</span><<span style="color: blue;">int</span>> _subject = <span style="color: blue;">new</span> <span style="color: #2b91af;">Subject</span><<span style="color: blue;">int</span>>();<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"></pre>
<span style="color: blue;">public</span> PriceSource(<span style="color: blue;">string</span> symbol)
{
_symbol = symbol;
}</pre>
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;"> public</span> <span style="color: #2b91af;">IObservable</span><<span style="color: blue;">int</span>> Multicast { <span style="color: blue;">get</span>; <span style="color: blue;">private</span> <span style="color: blue;">set</span>; }</pre>
</pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;">}</pre>
The class level variable <i>_subject </i>will be used shortly to tie together a new observer to our source provider. The subject acts as observable sequence and an observer.<br />
<br />
The <span style="background-color: white; font-family: Consolas; font-size: 13px;"><i>Multicast</i> </span><span style="background-color: white;">property is be the mechanism by which we expose access to an </span><span style="color: #2b91af; font-family: Consolas; font-size: 13px;">IObservable</span><span style="background-color: white;">.</span><span style="background-color: white;"> For the sake of this demo we're providing access to an </span><span style="background-color: white; color: blue; font-family: Consolas; font-size: 13px;">int </span><span style="background-color: white;">which represents a ticking price.</span><br />
<br />
To the body of the constructor, add this line: <span style="background-color: white; font-family: Consolas; font-size: 13px;">Multicast = CreateMulticast();</span><br />
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">public</span> PriceSource(<span style="color: blue;">string</span> symbol)
{
_symbol = symbol;
Multicast = CreateMulticast();
}</pre>
</pre>
Now add the body of <span style="background-color: white; font-family: Consolas; font-size: 13px;">CreateMulticast:</span><br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">private</span> <span style="color: #2b91af;">IObservable</span><<span style="color: blue;">int</span>> CreateMulticast()
{
<span style="color: blue;">const</span> <span style="color: blue;">int</span> NumberOfTicksToReplay = 1;
<span style="color: green;">// Use the static Create method so that we can define our own</span>
<span style="color: green;">// implementation for the return value of the Subscribe function</span>
<span style="color: blue;">return</span> <span style="color: #2b91af;">Observable</span>.Create<<span style="color: blue;">int</span>>(observer =>
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">: Create underlying observable..."</span>, _symbol);
CreateUnderlyingSubscription();
<span style="color: green;">// create a new observable based on our source provider</span>
<span style="color: blue;">var</span> newSubscription = _subject.Subscribe(observer);
<span style="color: green;">// wrap in a dispose action, which will be called once the refcount gets to zero</span>
<span style="color: blue;">var</span> unsubscriber = <span style="color: #2b91af;">Disposable</span>.Create(() =>
{
<span style="color: green;">// Do release bit </span>
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">: RefCount - all subscriptions disposed. calling Release..."</span>, _symbol);
ReleaseUnderlyingSubscription();
});
<span style="color: blue;">return</span> <span style="color: blue;">new</span> <span style="color: #2b91af;">CompositeDisposable</span>(newSubscription, unsubscriber);
})
.Replay(NumberOfTicksToReplay)
.RefCount();
}</pre>
</pre>
<span style="background-color: white; font-family: Consolas; font-size: 13px;"><br /></span>
There's quite a bit going on. Essentially we're using the <span style="font-family: Courier New, Courier, monospace;">Observable.Create</span> static method to allow us return an IDisposable from a specific Subscribe implementation.<br />
<br />
We'll add <span style="font-family: Consolas; font-size: 13px;">CreateUnderlyingSubscription() </span><span style="font-family: inherit;">soon. This gets called each time a new source provider needs to be created. You might delegate that to a provider factory which knows how to subscribe to a service.</span><br />
<br />
Here we're telling the source provider subject (<span style="font-family: Consolas; font-size: 13px;">_subject</span>) that the new subscription should receive notifications:<br />
<br />
<span style="color: blue; font-family: Consolas; font-size: 13px;">var</span><span style="font-family: Consolas; font-size: 13px;"> newSubscription = _subject.Subscribe(observer);</span><span style="font-family: inherit;"> </span><br />
<br />
Note that <span style="font-family: Consolas; font-size: 13px;">_subject </span>is a plain <span style="color: #2b91af; font-size: 13px;">Subject</span><span style="background-color: white; font-size: 13px;"><</span><span style="color: blue; font-size: 13px;">int</span><span style="background-color: white; font-size: 13px;">></span>. We don't need to use a <span style="font-family: Courier New, Courier, monospace;">ReplaySubject </span>or <span style="font-family: Courier New, Courier, monospace;">BehaviorSubject </span>in our own implementation (see below)<br />
<br />
Next we make use of <span style="font-family: Courier New, Courier, monospace;">Disposable.Create</span> (from <span style="background-color: white; font-family: Consolas; font-size: 13px;">System.Reactive.Disposables). </span><br />
<br />
<i>Creates a disposable object that invokes the specified action when disposed</i><br />
<span style="font-family: Courier New, Courier, monospace;">Disposable.Create(Action dispose) </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
This static method returns<span style="font-family: Consolas;"><span style="background-color: white; font-size: 13px;"> </span></span>an object of type <span style="font-family: Courier New, Courier, monospace;">IDisposable </span>and performs the action passed when the <span style="font-family: Courier New, Courier, monospace;">Dispose </span>method is called. As expected, no matter how many times the Dispose method is called the <i>dispose </i>Action will called just once - the very first time (see <a href="http://en.wikipedia.org/wiki/Idempotence" target="_blank">Idempotence</a>)<br />
<br />
By combining this with the .RefCount extension we will be told when it's ready to release the underlying provider's subscription, in our case we'll call <span style="font-family: Consolas; font-size: 13px;">ReleaseUnderlyingSubscription()</span><br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">The body needs to return an IDisposable, so we make use of the </span><span style="background-color: transparent; color: #2b91af;">CompositeDisposable</span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">. Any call to Dispose on the </span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;"> </span><span style="background-color: transparent; color: #2b91af;">CompositeDisposable</span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;"> will result in Dispose() being called on both </span><span style="background-color: transparent;">newSubscription </span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">and</span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;"> </span><span style="background-color: transparent;">unsubscriber </span><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">which RefCount needs in order to track IObservable usage.</span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="background-color: transparent;"> </span></pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">The last two statements, </span>.Replay() <span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">and .</span>RefCount()<span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;"> are key to our requirements.</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<blockquote class="tr_bq">
<span style="background-color: transparent; white-space: normal;"><span style="font-family: Times New Roman;"><i>Returns a connectable observable sequence that shares a single subscription to the underlying sequence replaying all notifications</i></span></span><span style="white-space: normal;"><span style="font-family: Courier New, Courier, monospace;">Observable.Replay</span></span></blockquote>
</pre>
<pre style="background: white;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial;"><span style="white-space: normal;"><span style="font-family: Times New Roman;">It's an extension method found in System.Reactive.Linq namespace. It </span></span><span style="font-family: Times New Roman;"><span style="white-space: normal;">returns an IConnectableObservable<T> which extends IObservable<T> to include a Connect() method. I won't go into much more detail here, Connect() is used for sharing of data.</span></span><span style="font-family: 'Times New Roman'; white-space: normal;"> </span></pre>
</pre>
<span style="background-color: white;"><br /></span>
<span style="background-color: white;">The particular overload that we are using is passed an integer - that's the size of a replay buffer that we want replied to any new observer. Here we're passing 1 as we only want the last price to be replayed. Under the hood Replay makes use of a ReplaySubject - the reason why we did not need to use that as our type for </span><span style="background-color: white; font-size: 13px;">_subject.</span><br />
<span style="background-color: white; font-size: 13px;"><br /></span>
<span style="background-color: white;">Moving onto the RefCount. It is an </span><span style="background-color: white;">IConnectableObservable<T> </span><span style="background-color: white;">extension method - </span><i>returns an observable sequence that stays connected to the source as long as there is at least one subscription to the observable sequence.</i><br />
<i><br /></i>
<span style="background-color: white;">This does all the hard work of tracking calls to Dispose() on the objects that we created in </span><span style="font-size: 13px;">CreateMulticast</span><span style="background-color: white;"> - it's as simple as that.</span><br />
<i><br /></i>
<span style="background-color: white;">All that remains to be done is to add the two remaining methods - remember these are just dummy methods in this demo with a bit of logging</span><br />
<span style="background-color: white;"><br /></span>
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">private</span> <span style="color: blue;">void</span> CreateUnderlyingSubscription()
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">: Created underlying subscription"</span>, _symbol);
}
<span style="color: blue;">private</span> <span style="color: blue;">void</span> ReleaseUnderlyingSubscription()
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">: Released underlying subscription"</span>, _symbol); </pre>
<span style="background-color: white; font-family: Consolas; font-size: 13px;">}</span><span style="background-color: white;"> </span><br />
<span style="background-color: white;"><br /></span>
<span style="background-color: white;"><b></b></span><br />
<span style="background-color: white;"><b><br /></b></span>
<br />
<h3>
<span style="background-color: white;"><b>Completed Code</b></span> </h3>
<br />
<span style="background-color: white;">The completed code should look like this.</span><br />
<span style="background-color: white;">Notice I've sneaked in a new method </span><span style="background-color: orange;"><b><span style="color: blue; font-family: Consolas; font-size: 13px;">void</span><span style="font-family: Consolas; font-size: 13px;"> NotifyPriceChange(</span><span style="color: blue; font-family: Consolas; font-size: 13px;">int</span></b><span style="font-family: Consolas; font-size: 13px;"><b> newPrice)</b>.</span></span><br />
<span style="background-color: white;">I've added this so I can simulate the underlying provider sending us a new price for this source for the unit tests...clearly you'd delegate that down to the market data service. </span><br />
<br />
<span style="background-color: white;"></span>
<br />
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> System.Reactive.Disposables;
<span style="color: blue;">using</span> System.Reactive.Linq;
<span style="color: blue;">using</span> System.Reactive.Subjects;
<span style="color: blue;">public</span> <span style="color: blue;">class</span> <span style="color: #2b91af;">PriceSource</span>
{
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: #2b91af;">ISubject</span><<span style="color: blue;">int</span>> _subject = <span style="color: blue;">new</span> <span style="color: #2b91af;">Subject</span><<span style="color: blue;">int</span>>();
<span style="color: blue;">private</span> <span style="color: blue;">readonly</span> <span style="color: blue;">string</span> _symbol;
<span style="color: blue;">public</span> PriceSource(<span style="color: blue;">string</span> symbol)
{
_symbol = symbol;
Multicast = CreateMulticast();
}
</pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><b><span style="background-color: orange;"> <span style="color: green;">// Here for use with unit tests to simulate prices flowing in</span>
<span style="color: blue;">internal</span> <span style="color: blue;">void</span> NotifyPriceChange(<span style="color: blue;">int</span> newPrice)
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"</span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">: price changed to </span><span style="color: mediumseagreen;">{1}</span><span style="color: #a31515;">"</span>, _symbol, newPrice);
_subject.OnNext(newPrice);
}</span><span style="background-color: white;">
</span></b><span style="background-color: white;">
</span><span style="background-color: white; color: blue;">public</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">IObservable</span><span style="background-color: white;"><</span><span style="background-color: white; color: blue;">int</span><span style="background-color: white;">> Multicast { </span><span style="background-color: white; color: blue;">get</span><span style="background-color: white;">; </span><span style="background-color: white; color: blue;">private</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">set</span><span style="background-color: white;">; }
</span><span style="background-color: white; color: blue;">private</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">IObservable</span><span style="background-color: white;"><</span><span style="background-color: white; color: blue;">int</span><span style="background-color: white;">> CreateMulticast()
{
</span><span style="background-color: white; color: blue;">const</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">int</span><span style="background-color: white;"> NumberOfTicksToReplay = 1;
</span><span style="background-color: white; color: green;">// Use the static Create method so that we can define our own</span><span style="background-color: white;">
</span><span style="background-color: white; color: green;">// implementation for the return value of the Subscribe function</span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;">return</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">Observable</span><span style="background-color: white;">.Create<</span><span style="background-color: white; color: blue;">int</span><span style="background-color: white;">>(observer =>
{
</span><span style="background-color: white; color: #2b91af;">Console</span><span style="background-color: white;">.WriteLine(</span><span style="background-color: white; color: #a31515;">"</span><span style="background-color: white; color: mediumseagreen;">{0}</span><span style="background-color: white; color: #a31515;">: Create underlying observable..."</span><span style="background-color: white;">, _symbol);
CreateUnderlyingSubscription();
</span><span style="background-color: white; color: green;">// create a new observable based on our source provider</span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> newSubscription = _subject.Subscribe(observer);
</span><span style="background-color: white; color: green;">// wrap in a dispose action, which will be called once the refcount gets to zero</span><span style="background-color: white;">
</span><span style="background-color: white; color: blue;">var</span><span style="background-color: white;"> unsubscriber = </span><span style="background-color: white; color: #2b91af;">Disposable</span><span style="background-color: white;">.Create(() =>
{
</span><span style="background-color: white; color: green;">// Do release bit </span><span style="background-color: white;">
</span><span style="background-color: white; color: #2b91af;">Console</span><span style="background-color: white;">.WriteLine(</span><span style="background-color: white; color: #a31515;">"</span><span style="background-color: white; color: mediumseagreen;">{0}</span><span style="background-color: white; color: #a31515;">: RefCount - all subscriptions disposed. calling Release..."</span><span style="background-color: white;">, _symbol);
ReleaseUnderlyingSubscription();
});
</span><span style="background-color: white; color: blue;">return</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">new</span><span style="background-color: white;"> </span><span style="background-color: white; color: #2b91af;">CompositeDisposable</span><span style="background-color: white;">(newSubscription, unsubscriber);
})
.Replay(NumberOfTicksToReplay)
.RefCount();
}
</span><span style="background-color: white; color: blue;">private</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">void</span><span style="background-color: white;"> CreateUnderlyingSubscription()
{
</span><span style="background-color: white; color: #2b91af;">Console</span><span style="background-color: white;">.WriteLine(</span><span style="background-color: white; color: #a31515;">"</span><span style="background-color: white; color: mediumseagreen;">{0}</span><span style="background-color: white; color: #a31515;">: Created underlying subscription"</span><span style="background-color: white;">, _symbol);
}
</span><span style="background-color: white; color: blue;">private</span><span style="background-color: white;"> </span><span style="background-color: white; color: blue;">void</span><span style="background-color: white;"> ReleaseUnderlyingSubscription()
{
</span><span style="background-color: white; color: #2b91af;">Console</span><span style="background-color: white;">.WriteLine(</span><span style="background-color: white; color: #a31515;">"</span><span style="background-color: white; color: mediumseagreen;">{0}</span><span style="background-color: white; color: #a31515;">: Released underlying subscription"</span><span style="background-color: white;">, _symbol);
}
}
</span></pre>
<pre style="background: white; font-family: Consolas;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">That's it. How easy was that? </span></pre>
<pre style="background: white; font-family: Consolas;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<h3 style="background: white; font-family: Consolas;">
<span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">Tests</span></h3>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">How about some unit tests to see this in place? I'm using the NUnit framework in conjunction with Resharper to easily run my unit tests in </span></pre>
<pre style="background: white; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
<pre style="background: white;"><span style="white-space: normal;"><span style="font-family: Courier New, Courier, monospace;">Install-Package NUnit</span></span></pre>
<pre style="background: white;"><span style="white-space: normal;"><span style="font-family: Courier New, Courier, monospace;">
</span></span></pre>
<pre style="background: white;"><span style="white-space: normal;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">Purely for the sake of clarity, I've added a whole bunch of rather verbose Arrange-Act-Assert calls into one big test :(</span></pre>
<pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><span style="font-family: 'Times New Roman'; font-size: small; white-space: normal;">
</span></pre>
</span></pre>
<pre style="background: white;"><span style="white-space: normal;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas; font-size: 13px;"><pre style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: Consolas;"><span style="color: blue;">using</span> System;
<span style="color: blue;">using</span> NUnit.Framework;
[<span style="color: #2b91af;">TestFixture</span>]
<span style="color: blue;">class</span> <span style="color: #2b91af;">PriceSourceTests</span>
{
[<span style="color: #2b91af;">Test</span>]
<span style="color: blue;">public</span> <span style="color: blue;">void</span> MultipleSubscribes_AllActive_ReceiveSamePrice()
{
<span style="color: green;">// ARRANGE</span>
<span style="color: blue;">var</span> source = <span style="color: blue;">new</span> <span style="color: #2b91af;">PriceSource</span>(<span style="color: #a31515;">"MSFT"</span>);
<span style="color: blue;">var</span> subs1Price = 0;
<span style="color: blue;">var</span> subs1Ticks = 0;
<span style="color: blue;">var</span> subs1 = source.Multicast.Subscribe(price =>
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"Observer 1 received </span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">"</span>, price);
subs1Ticks++;
subs1Price = price;
});
<span style="color: blue;">var</span> subs2Price = 0;
<span style="color: blue;">var</span> subs2Ticks = 0;
<span style="color: blue;">var</span> subs2 = source.Multicast.Subscribe(price =>
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"Observer 2 received </span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">"</span>, price);
subs2Ticks++;
subs2Price = price;
});
<span style="color: green;">// ACT</span>
source.NotifyPriceChange(100);
<span style="color: green;">// ASSERT</span>
<span style="color: #2b91af;">Assert</span>.That(subs1Price, <span style="color: #2b91af;">Is</span>.EqualTo(100));
<span style="color: #2b91af;">Assert</span>.That(subs1Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(1));
<span style="color: #2b91af;">Assert</span>.That(subs2Price, <span style="color: #2b91af;">Is</span>.EqualTo(100));
<span style="color: #2b91af;">Assert</span>.That(subs2Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(1));
<span style="color: green;">// ACT</span>
source.NotifyPriceChange(200);
<span style="color: green;">// ASSERT</span>
<span style="color: #2b91af;">Assert</span>.That(subs1Price, <span style="color: #2b91af;">Is</span>.EqualTo(200));
<span style="color: #2b91af;">Assert</span>.That(subs1Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(2));
<span style="color: #2b91af;">Assert</span>.That(subs2Price, <span style="color: #2b91af;">Is</span>.EqualTo(200));
<span style="color: #2b91af;">Assert</span>.That(subs2Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(2));
<span style="color: green;">// ACT</span>
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"Disposing subs1"</span>);
subs1.Dispose();
source.NotifyPriceChange(300);
<span style="color: green;">// ASSERT</span>
<span style="color: #2b91af;">Assert</span>.That(subs1Price, <span style="color: #2b91af;">Is</span>.EqualTo(200));
<span style="color: #2b91af;">Assert</span>.That(subs1Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(2));
<span style="color: #2b91af;">Assert</span>.That(subs2Price, <span style="color: #2b91af;">Is</span>.EqualTo(300));
<span style="color: #2b91af;">Assert</span>.That(subs2Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(3));
<span style="color: green;">// ACT</span>
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"Disposing subs2"</span>);
subs2.Dispose();
<span style="color: green;">// ASSERT</span>
<span style="color: #2b91af;">Assert</span>.That(subs1Price, <span style="color: #2b91af;">Is</span>.EqualTo(200));
<span style="color: #2b91af;">Assert</span>.That(subs1Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(2));
<span style="color: #2b91af;">Assert</span>.That(subs2Price, <span style="color: #2b91af;">Is</span>.EqualTo(300));
<span style="color: #2b91af;">Assert</span>.That(subs2Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(3));
<span style="color: green;">// ACT</span>
<span style="color: blue;">var</span> subs3Price = 0;
<span style="color: blue;">var</span> subs3Ticks = 0;
source.Multicast.Subscribe(price =>
{
<span style="color: #2b91af;">Console</span>.WriteLine(<span style="color: #a31515;">"Observer 3 received </span><span style="color: mediumseagreen;">{0}</span><span style="color: #a31515;">"</span>, price);
subs3Ticks++;
subs3Price = price;
});
<span style="color: green;">// ASSERT</span>
<span style="color: #2b91af;">Assert</span>.That(subs3Price, <span style="color: #2b91af;">Is</span>.EqualTo(300));
<span style="color: #2b91af;">Assert</span>.That(subs3Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(1));
<span style="color: green;">// ACT</span>
source.NotifyPriceChange(400);
<span style="color: green;">// ASS</span>
<span style="color: #2b91af;">Assert</span>.That(subs3Price, <span style="color: #2b91af;">Is</span>.EqualTo(400));
<span style="color: #2b91af;">Assert</span>.That(subs3Ticks, <span style="color: #2b91af;">Is</span>.EqualTo(2));
}
}</pre>
</pre>
</span></pre>
<h3>
<b>Test Results</b></h3>
<span style="font-family: Courier New, Courier, monospace;">MSFT: Create underlying observable...</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: Created underlying subscription</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: price changed to 100</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 1 received 100</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 2 received 100</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: price changed to 200</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 1 received 200</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 2 received 200</span><br />
<span style="font-family: Courier New, Courier, monospace;">Disposing subs1</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: price changed to 300</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 2 received 300</span><br />
<span style="font-family: Courier New, Courier, monospace;">Disposing subs2</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: RefCount - all subscriptions disposed. calling Release...</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: Released underlying subscription</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 3 received 300</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: Create underlying observable...</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: Created underlying subscription</span><br />
<span style="font-family: Courier New, Courier, monospace;">MSFT: price changed to 400</span><br />
<span style="font-family: Courier New, Courier, monospace;">Observer 3 received 400</span><br />
<br />
<br /></div>
Jason Haleshttp://www.blogger.com/profile/00333765945492399088noreply@blogger.com2