I was having a Skype conversation with Pete Brown and he said “Why don't you blog this stuff, or set up a GitHub project or something with these things?”. I told him, “well, actually, I just started blogging!”
That conversation gave me a figurative kick in the tuchus, so I decided to share a small nugget.
Defensive Development
- Checking all your assumptions
- Checking all your parameters for null or incorrect values
- Verifying things like loop invariants, preconditions, postconditions, http://en.wikipedia.org/wiki/Code_contract, etc
Given that I think like this, I tend to use debug assertions a lot.
Debug.Assert
So, Debug.Assert is useful, but, what about Release mode?
What about Release Mode?
I think this is Utopian in this day and age.
For the large majority of developers, extreme performance is not a problem. A few exceptions would be developers who work on:
- BIG games (not words with friends, more like Call Of Duty)
- Operating Systems
- Low-level Device Drivers
Release.Assert?
Of course, you wouldn’t run Debug code in Production either… Right? RIGHT?
So, our only option is Exceptions.
My Solution
- continue to utilize Debug.Assert
- In Release Mode, throw an exception instead.
- Allow for custom exception usage (can’t just throw one kind of exception)
[SuppressMessage("Microsoft.Design", "CA1004", Justification = 
  "This method instantiates and throws the exception, it can't be passed in")]
[DebuggerStepThrough()]
public static void AssertThrow<TException>(bool condition, 
   string failureMessage) where TException : Exception
{
    Debug.Assert(condition, failureMessage);
    if (condition == false)
    {
        // throw exception here...
    }
}
The [SuppressMessage] is there in case you’re running Code Analysis.  The analysis tools don’t like that the method is using a generic type that’s not part of the rest of the function signature.
The [DebuggerStepThrough] just makes it so that it steps over this as if it’s one execution statement when debugging.
The Debug.Assert line is rather obvious, so I’ll skip that.
The "where TException : Exception" part forces the type you specify to be a derived class of System.Exception, so it can be thrown.
The next section is where we throw the exception.
ConstructorInfo ctor = typeof(TException).GetConstructor(
   new Type[] { typeof(string) });
TException ex = (TException)ctor.Invoke(new object[] { failureMessage });
throw ex;
I’m using Reflection to get the constructor of the TException object that takes one string. Then, I am invoking that constructor with the failureMessage and throwing the exception.
So, all you do is call it like this:
void Foo(Bar bar)
{
    Diag.AssertThrow<ArgumentNullException>(
        bar != null, "Duh, Foo needs a Bar!");
        
    // use bar here.
}
Teh Codez
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
namespace McKearney.Common.Diagnostics
{
    /// <summary>
    /// Utility class that contains Diagnostic- and Instrumentation-related methods
    /// </summary>
    public static class Diag
    {
        #region Static Operations
        /// <summary>
        /// Asserts and then throws, making sure that the error gets handled by client code
        /// </summary>
        /// <typeparam name="TException">The type of the exception to throw.</typeparam>
        /// <param name="condition">the asserted condition</param>
        /// <param name="failureMessage">The failure message.</param>
        [SuppressMessage("Microsoft.Design", "CA1004", Justification = "This method instantiates and throws the exception, it can't be passed in")]
        [DebuggerStepThrough()]
        public static void AssertThrow<texception>(bool condition, string failureMessage) where TException : Exception
        {
            Debug.Assert(condition, failureMessage);
            if (condition == false)
            {
                ConstructorInfo ctor = typeof(TException).GetConstructor(new Type[] { typeof(string) });
                TException ex = (TException)ctor.Invoke(new object[] { failureMessage });
                throw ex;
            }
        }
    }
}
Leave me a comment, I'm curious what your thoughts are.
 
No comments:
Post a Comment