Friday, 21 June 2013

Why you should check event delegates for null

The other day I came across some code that was raising an event like this:
    public class Dummy
    {
        public event EventHandler SomethingHappened;
        public void RaiseEvent()
        {
            try
            {
                SomethingHappened(this, EventArgs.Empty);
            }
            catch { }
        }
    }
As opposed to the recognised pattern of
    public class Dummy
    {
        public event EventHandler SomethingHappened;
        public void RaiseEvent()
        {
            if (SomethingHappened != null)
            {
                SomethingHappened(this, EventArgs.Empty);
            }
        }
    }
Chatting to a friend at the pub, I mentioned about this code and the fact that the code was relying on exception handling to control the flow, and he mentioned that it would be a lot slower if there wasn't an event handler defined. I knew it would be slower to create, throw and catch a NullReferenceException than to perform a null check, but I hadn't realised just how much slower.

To prove it, I created a class that contains a single event, and exposed two methods to raise the event - one with a try/catch block and one with a null check
    public class Dummy
    {
        public event EventHandler SomethingHappened;

        public void RaiseWithNullCheck()
        {
            if (SomethingHappened != null)
            {
                SomethingHappened(this, EventArgs.Empty);
            }
        }

        public void RaiseInTryCatch()
        {
            try
            {
                SomethingHappened(this, EventArgs.Empty);
            }
            catch { }
        }
    }
...and a console app to repeatedly run the methods with no event handler defined
        public static void Main(string[] args)
        {
            var dummy = new Dummy();
            var stopwatch = Stopwatch.StartNew();
            var iterations = 10000;

            for (int i = 0; i < iterations; ++i)
            {
                dummy.RaiseWithNullCheck();
            }
            stopwatch.Stop();
            Console.WriteLine("Null check: {0}", stopwatch.ElapsedMilliseconds);

            stopwatch.Restart();
            for (int i = 0; i < iterations; ++i)
            {
                dummy.RaiseInTryCatch();
            }
            stopwatch.Stop();
            Console.WriteLine("Try catch: {0}", stopwatch.ElapsedMilliseconds);

            Console.ReadLine();
        }
This is a screenshot of the output - note the values are milliseconds
That's right. Sub-millisecond for the 10,000 null checks, and over 36 seconds for the 10,000 exceptions.


The remaining question is "what's the difference in timing if there is a handler defined for the event?" Obviously in this case the try/catch will be quicker because there's no overhead of checking for null before invoking the event handler. I had to massage the code a little though, before the difference was noticeable:
        public static void Main(string[] args)
        {
            var dummy = new Dummy();
            // Add an empty event handler
            dummy.SomethingHappened += (s, e) => { };

            var stopwatch = Stopwatch.StartNew();
            var iterations = 10000;

            for (int i = 0; i < iterations; ++i)
            {
                dummy.RaiseWithNullCheck();
            }
            stopwatch.Stop();

            // report in ticks, not milliseconds
            Console.WriteLine("Null check: {0} ticks", stopwatch.ElapsedTicks);

            stopwatch.Restart();
            for (int i = 0; i < iterations; ++i)
            {
                dummy.RaiseInTryCatch();
            }
            stopwatch.Stop();
            Console.WriteLine("Try catch: {0} ticks", stopwatch.ElapsedTicks);

            Console.ReadLine();
        }
Here's a typical output for this one (the timings vary a little between runs). Note that this is in ticks not milliseconds (a tick being 1/10,000 of a millisecond).
Given the tiny numbers when the event handler is defined, there isn't a good reason to rely on a try/catch when a simple null check will do.

Sunday, 16 June 2013

Make your Gadgeteer app testable with the MVP pattern

As Gadgeteer is very much about hardware, it's not always obvious how to write unit tests for the code. This post will go through the mechanics of the MVP pattern. The example app cconsists of a button and a multicolour LED; when the button is pressed, the LED displays a random colour. If you don't believe me, there's a video.

The MVP pattern

The Model-View-Presenter (MVP) pattern is designed to split the UI (View) from the business logic (Model) via a class that sends messages between the two (Presenter). In the implementation I use (Passive View) the view has little or, if possible, no logic. When an event occurs (e.g. a button press), the view raises an event which is handled by the presenter. The presenter then invokes a void method on the model. If required, the model will raise an event to signal that the work is completed. The presenter handles that event too, and calls a method on the view to cause a UI update to occur.

Here's the sequence that occurs in the example app:

The Classes

The solution consists of three projects: the Gadgeteer project, the class library and the test project.
The class library defines the classes and interfaces required to implement the MVP pattern - the model, the presenter and the view interface.
The IView interface declares an event ButtonPressed, to be handled by the presenter, and a method ShowColour that will be called by the presenter:

using Microsoft.SPOT;

namespace ButtonAndLight.Lib
{
    public delegate void ButtonPressedEventHandler(object sender, EventArgs args);

    public interface IView
    {
        // The event that the UI will raise when the button is pressed
        event ButtonPressedEventHandler ButtonPressed;
        
        //The method the presenter will call when the colour needs to change
        void ShowColour(byte r, byte g, byte b);
    }    
}

The Model class declares an event ColourChanged, to be handled by the presenter, and a method CalculateNewColour, to be called by the presenter:
using Microsoft.SPOT;

namespace ButtonAndLight.Lib
{
    public class Model
    {
        public Model()
            : this(new RandomGenerator())
        { }

        public Model(IRandomGenerator randomGenerator)
        {
            _randomGenerator = randomGenerator;
        }

        private IRandomGenerator _randomGenerator;

        // Old school event handler for the Micro Framework
        public delegate void ColourChangedEventHandler(object sender, ColourChangedEventArgs args);

        // The event that will be raised when the colour has changed
        public event ColourChangedEventHandler ColourChanged;

        // This will be called by the presenter
        public void CalculateNewColour()
        {
            var r = _randomGenerator.GetNextColourPart();
            var g = _randomGenerator.GetNextColourPart();
            var b = _randomGenerator.GetNextColourPart();

            OnColourChanged(r, g, b);
        }

        private void OnColourChanged(byte r, byte g, byte b)
        {
            if (ColourChanged != null)
            {
                ColourChanged(this, new ColourChangedEventArgs(r, g, b));
            }
        }
    }

    public class ColourChangedEventArgs : EventArgs
    {
        public byte R { get; private set; }
        public byte G { get; private set; }
        public byte B { get; private set; }
        public ColourChangedEventArgs(byte r, byte g, byte b)
        {
            R = r;
            G = g;
            B = b;
        }
    }
}
The Presenter class stitches the model and view together, by calling the relevant method when an event occurs:
using Microsoft.SPOT;

namespace ButtonAndLight.Lib
{
    public class Presenter
    {
        IView _view;
        Model _model;

        public Presenter(IView view, Model model)
        {
            _view = view;
            _model = model;

            _view.ButtonPressed += new ButtonPressedEventHandler(view_ButtonPressed);
            _model.ColourChanged += new Model.ColourChangedEventHandler(model_ColourChanged);
        }

        void model_ColourChanged(object sender, ColourChangedEventArgs args)
        {
            _view.ShowColour(args.R, args.G, args.B);
        }

        void view_ButtonPressed(object sender, EventArgs args)
        {
            _model.CalculateNewColour();
        }
    }
}
These are the Gadgeteer components:

The program class in the Gadgeteer project needs to implement the IView interface, so it can pass a reference to itself to the presenter. In this example I've also made it responsible for building the presenter:

using ButtonAndLight.Lib;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT;
using Gadgeteer.Modules.GHIElectronics;

namespace ButtonAndLight
{
    public partial class Program : IView
    {
        private Presenter _presenter;

        void ProgramStarted()
        {
            _presenter = new Presenter(this, new Model());

            // Wire up handler for the the physical button press
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            if (ButtonPressed != null)
            {
                ButtonPressed(this, EventArgs.Empty);
            }
        }

        public event ButtonPressedEventHandler ButtonPressed;

        public void ShowColour(byte r, byte g, byte b)
        {
            var colour = ColorUtility.ColorFromRGB(r, g, b);
            multicolorLed.TurnColor(colour);
        }
    }
}
All that's left now is the test class. As this is the Micro Framework, I've had to do by hand what I'd normally have done with Rhino and NUnit
using System;
using Microsoft.SPOT;

namespace ButtonAndLight.Lib.Test
{
    public class Program
    {
        public static void Main()
        {
            ButtonPressed_ExpectRandomColourGenerated();
        }

        private static void ButtonPressed_ExpectRandomColourGenerated()
        {
            // Create objects
            var view = new FakeView();
            var model = new Model(new FakeRandomGenerator());
            var presenter = new Presenter(view, model);

            // Raise event
            view.RaiseButtonPressEvent();

            // Check result
            if (view.R == 10 && view.G == 20 && view.B == 30)
            {
                Debug.Print("Success");
                return;
            }

            throw new InvalidOperationException("Failure");
        }
    }

    public class FakeView : IView
    {
        // Sensing variables
        public byte R { get; private set; }
        public byte G { get; private set; }
        public byte B { get; private set; }

        // Pretend a button's been pushed
        public void RaiseButtonPressEvent()
        {
            if (ButtonPressed != null)
            {
                ButtonPressed(this, EventArgs.Empty);
            }
        }

        #region IView interface
        public event ButtonPressedEventHandler ButtonPressed;

        public void ShowColour(byte r, byte g, byte b)
        {
            R = r;
            G = g;
            B = b;
        }
        #endregion
    }

    public class FakeRandomGenerator : IRandomGenerator
    {
        byte[] _values = new byte[] { 10, 20, 30 };
        int _valuePointer = 0;

        public byte GetNextColourPart()
        {
            return _values[_valuePointer++];
        }
    }
}
Here's a video of the hardware in action:
video
The code for this example can be found on GitHub at https://github.com/orangutanboy/MVPExample/