I recently made a fix to a
Silverlight stopwatch class I wrote last
year.
In doing so, I decided to kick it up a notch.
using (new StopwatchPlus("Constructor"))
{
//Debug.WriteLine("Initialized: Ellapsed Ticks: {0}, Ellapsed Milliseconds: {1}", sw.EllapsedTicks, sw.EllapsedMilliseconds); InitializeComponent();
Thread.Sleep(600);
}
Instead of just a simple Stopwatch class, I added functionality to make it easy to
capture timings for blocks of code, output to the Debug Output window, and perform
any custom start/stop action.
StopwatchPlus sp1 = new StopwatchPlus(sw2 =>
Debug.WriteLine("Time! {0}", sw2.EllapsedMilliseconds));
Thread.Sleep(500);
sp1.Stop(); // this will call the stopAction defined in the constructor
In non-debug builds, the StopwatchPlus class does not send output to the debug
window. (I used the ConditionalAttribute … I know I could have used conditional
compilation statements, but I didn’t. :-) ).
using System;
using System.Diagnostics;
namespace WiredPrairie.Silverlight
{
/// <summary> /// StopwatchPlus is used to measure the general performance of Silverlight functionality. Silverlight /// does not provide a high resolution timer as is available in many operating systems, /// so the resolution of this timer is limited to milliseconds. This class is best used to measure /// the relative performance of functions over many iterations. /// </summary> public sealed class StopwatchPlus : IDisposable {
private long _startTick;
private long _elapsed;
private bool _isRunning;
private string _name;
private Action<StopwatchPlus> _startAction;
private Action<StopwatchPlus> _stopAction;
/// <summary> /// Creates an instance of the StopwatchPlus class and starts the timer. By /// providing a value to the name parameter, the Debug Console automatically /// will include the current values when the timer was started and stopped with /// this name. /// </summary> /// <param name="name"></param> public StopwatchPlus(string name)
: this(name, WriteStart, WriteResults) { }
/// <summary> /// Creates an instance of the StopwatchPlus class and starts the timer. /// </summary> /// <param name="stopAction">Action to take when the Stop method is called.</param> public StopwatchPlus(Action<StopwatchPlus> stopAction)
:this(null, stopAction)
{
}
/// <summary> /// Creates an instance of the StopwatchPlus class and starts the timer. /// </summary> /// <param name="startAction">Action to take when the timer starts.</param> /// <param name="stopAction">Action to take when the Stop method is called.</param> public StopwatchPlus(Action<StopwatchPlus> startAction,
Action<StopwatchPlus> stopAction)
:this(null, startAction, stopAction)
{
}
/// <summary> /// Creates an instance of the StopwatchPlus class and starts the timer. /// </summary> /// <param name="name">Name of this stop watch (used as output)</param> /// <param name="startAction">Action to take when the timer starts.</param> /// <param name="stopAction">Action to take when the Stop method is called.</param> public StopwatchPlus(string name,
Action<StopwatchPlus> startAction,
Action<StopwatchPlus> stopAction)
{
_name = name;
_startAction = startAction;
_stopAction = stopAction;
Start();
}
public string Name
{
get { return _name; }
}
/// <summary> /// Completely resets and deactivates the timer. /// </summary> public void Reset()
{
_elapsed = 0;
_isRunning = false;
_startTick = 0;
}
/// <summary> /// Begins the timer. /// </summary> public void Start()
{
if (!_isRunning)
{
_startTick = GetCurrentTicks();
_isRunning = true;
if (_startAction != null)
{
_startAction(this);
}
}
}
/// <summary> /// Stops the current timer. /// </summary> public void Stop()
{
if (_isRunning)
{
_elapsed += GetCurrentTicks() - _startTick;
_isRunning = false;
if (_stopAction != null)
{
_stopAction(this);
}
}
}
/// <summary> /// Gets a value indicating whether the instance is currently recording. /// </summary> public bool IsRunning
{
get { return _isRunning; }
}
/// <summary> /// Gets the Ellapsed time as a Timespan. /// </summary> public TimeSpan Ellapsed
{
get { return TimeSpan.FromMilliseconds(EllapsedMilliseconds); }
}
/// <summary> /// Gets the Ellapsed time as the total number of milliseconds. /// </summary> public long EllapsedMilliseconds
{
get { return GetCurrentElapsedTicks() / TimeSpan.TicksPerMillisecond; }
}
/// <summary> /// Gets the Ellapsed time as the total number of ticks (which is faked /// as Silverlight doesn't have a way to get at the actual "Ticks") /// </summary> public long EllapsedTicks
{
get { return GetCurrentElapsedTicks(); }
}
private long GetCurrentElapsedTicks()
{
return (long)(this._elapsed + (IsRunning ? (GetCurrentTicks() - _startTick) : 0));
}
private long GetCurrentTicks()
{
// TickCount: Gets the number of milliseconds elapsed since the system started. return Environment.TickCount * TimeSpan.TicksPerMillisecond;
}
#region IDisposable Members
public void Dispose()
{
Stop();
}
#endregion private static void WriteStart(StopwatchPlus sw)
{
WriteStartInternal(sw);
}
// This is not called in a Release build [Conditional("DEBUG")]
private static void WriteStartInternal(StopwatchPlus sw)
{
Debug.WriteLine("BEGIN\t{0}", sw._name);
}
private static void WriteResults(StopwatchPlus sw)
{
WriteResultsInternal(sw);
}
// This is not called in a Release build [Conditional("DEBUG")]
private static void WriteResultsInternal(StopwatchPlus sw)
{
Debug.WriteLine("END\t{0}\t{1}", sw._name, sw.EllapsedMilliseconds);
}
}
}