Saturday, July 17, 2010

Generic ThrowIfNull (C#) Helper Method

July 17, 2010 Posted by Jason Irwin No comments

For a while now I’ve wanted to write a helper to beautify code like the following:

if(account == null)
{
throw new InvalidAccountException();
}




My code seems to be littered with such calls to the point that it feels like unnecessary bloat. While I’m sure there are many ways to get around this (I’d love to hear what they are) in this post I’ll describe a generic ThrowIfNull helper method I’ve created. I’ve seen similar attempts to my own where by default a helper method is used to throw an ArgumentNullException exception but I’ve wanted to be more specific with the exception being thrown. Essentially I want to pass an exception type and a value and, if the value is null, the exception type in question should be thrown. Specifically this is the desired signature:





ThrowIfNull<T>(Type exception, T value)




Obviously this isn’t a terribly difficult goal to achieve but previous efforts have led me to worry about the performance impact of using reflection to instantiate the desired exception at runtime. As it happens tonight I came across (related) posts by Roger Asling and Vagif Abilov citing performance differences when using compiled lambda expressions instead of using reflection (specifically Activator.CreateInstance and ConstructorInfo.Invoke) to instantiate objects at runtime.



This reading rekindled my desire to create a ThrowIfNull helper and, using Roger’s ObjectActivator function I was able to create a generic ThrowIfNull function that takes an exception type and a value. If the type is null, we instantiate an instance of the passed exception type. Essentially I get info for the standard String, Exception constructor, and pass it to the ObjectActivator function. This function creates an expression that will call the constructor using the parameters we defined. A Lambda is created using this expression and then the lambda is compiled.



Once control returns to the ThrowIfNull method, the lambda is actually executed and is passed in the name of the type which or value uses (resulting in an “Account is null” exception message”). The code can be instantiated as follows:



 ExceptionHelpers.ThrowIfNull(typeof(InvalidAccountException), account);


Below is the entire class:



   public static class ExceptionHelpers
{
public static void ThrowIfNull<T>(Type exception, T value) where T : class
{
if (value == null)
{
var types = new Type[2];
types[0] = typeof(string);
types[1] = typeof(Exception);

var constructorInfo = exception.GetConstructor(types);
var createdActivator = GetActivator<Exception>(constructorInfo);

var instance = createdActivator(typeof(T).Name + " is null", null);
throw instance;
}
}

private delegate T ObjectActivator<T>(params object[] args);

private static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
{
Type type = ctor.DeclaringType;
ParameterInfo[] paramsInfo = ctor.GetParameters();

//create a single param of type object[]
ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

var argsExp = new Expression[paramsInfo.Length];

//pick each arg from the params array
//and create a typed expression of them
for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;
Expression paramAccessorExp = Expression.ArrayIndex(param, index);
Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
argsExp[i] = paramCastExp;
}

//make a NewExpression that calls the
//ctor with the args we just created
var newExp = Expression.New(ctor, argsExp);

//create a lambda with the New
//Expression as body and our param object[] as arg
var lambda = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param);

//compile it
var compiled = (ObjectActivator<T>)lambda.Compile();
return compiled;
}

}


Right now all (anecdotal) evidence indicates that this method runs quite quickly but I plan to run some performance tests over the weekend to determine if this is truly the case.



I’d love to hear feedback as to whether or not this is an appropriate approach and what better approaches exist in the wild. Until next time…



[Update]



The obvious logical progression is to turn this helper into an extension method. The signature becomes:



ThrowIfNull<T>(this T value, Type exception)


Calling syntax becomes:



account.ThrowIfNull(typeof(InvalidAccountException));

0 comments: