Silverlight Stopwatch Class, Part 2

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);
        }
    }
}