Monday 23 June 2008

CheckForNull extension method

Debugging is much easier in C# if you remember to check arguments for null.
It's common to see code like this:

public static int Foo(object a, object b, object c)
{
    if (a == null)
    {
        throw new ArgumentNullException("a");
    }
    if (b == null)
    {
        throw new ArgumentNullException("b");
    }
if (c == null)
{
throw new ArgumentNullException("c");
}

    return 0;
}

I think it would be nice if instead we could write:

public static int Foo(object a, object b, object c)
{
    a.CheckForNull();
    b.CheckForNull();
    c.CheckForNull();
    return 0;
}

Well we can with the following [evil] extension method.

[MethodImpl(MethodImplOptions.NoInlining)]
public static void CheckForNull(this object o)
{
    if (o != null) return;
    StackFrame frame = new StackFrame(1, true);
    byte op = frame.GetMethod().GetMethodBody().GetILAsByteArray()[frame.GetILOffset() - 6];
    throw new ArgumentNullException(frame.GetMethod().GetParameters().Length <=
(4 - (frame.GetMethod().IsStatic ? 0 : 1)) ?
        frame.GetMethod().GetParameters()[op - (2 + (frame.GetMethod().IsStatic ? 0 : 1))].Name :
        "An argument was null, but I'm not sure which one.");
}

Is this a nice idea, an utterly horrible idea, or a bit of both?

What do you think?

kick it on DotNetKicks.com

1 comment:

Anonymous said...

This would be useful if it was not limited to, at worst 3 and at best 4 parameters to a function.

A nice idea, however not practical due to the limitations above.

Here is a slightly less obfuscated version of your code...

[MethodImpl(MethodImplOptions.NoInlining)]
public static void CheckForNull(this object o)
{
if (o != null)
return;

StackFrame frame = new StackFrame(1, true);
byte op = frame.GetMethod().GetMethodBody().GetILAsByteArray()[frame.GetILOffset() - 6];

string paramName = "An argument was null, but I'm not sure which one.";

if (frame.GetMethod().IsStatic)
{
if (frame.GetMethod().GetParameters().Length <= 4)
{
paramName = frame.GetMethod().GetParameters()[op - 4].Name;
}
}
else
{
if (frame.GetMethod().GetParameters().Length <= 3)
{
paramName = frame.GetMethod().GetParameters()[op - 3].Name;
}
}

throw new ArgumentNullException(paramName);
}