.NET Framework is a surprise box!!!

Most of the time when I need to get inspiration about how to do something, I turn to the inner bits and bolts of .NET Framework, because I think that there are a few hundred men-years worth of code there, and it does have some insights.

But, some times...

I was just looking into the Dictionary<TKey, TValue> class when I came across this little bit of code:

public virtual void GetObjectData
                       (SerializationInfo info, 
                        StreamingContext context)
{
  if (info == null)
  {
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.info);
  }
  // ...
}

Ok, we don't have much here, and inside the .NET Framework there are lots and lots of helper classes to aggregate common functions and adhere to the D.R.Y. principle.

However, when I dove into the ThrowArgumentNullException method, this is what I've found:

internal static void ThrowArgumentNullException
                        (ExceptionArgument argument)
{
  throw new ArgumentNullException(GetArgumentName(argument));
}

The ExceptionArgument type is actually an enumeration, that lists the possible values for the method argument. Well, I thought that perhaps there were more than meets the eye here, but when I went through the GetArgumentName function... take a look:

internal static string GetArgumentName
                          (ExceptionArgument argument)
{
  switch (argument)
  {
   case ExceptionArgument.obj:
        return "obj";

   case ExceptionArgument.dictionary:
        return "dictionary";

   case ExceptionArgument.dictionaryCreationThreshold:
        return "dictionaryCreationThreshold";

   case ExceptionArgument.array:
        return "array";

   case ExceptionArgument.info:
        return "info";

   case ExceptionArgument.key:
        return "key";

   case ExceptionArgument.collection:
        return "collection";

   case ExceptionArgument.list:
        return "list";

   case ExceptionArgument.match:
        return "match";

   case ExceptionArgument.converter:
        return "converter";

   case ExceptionArgument.queue:
        return "queue";

   case ExceptionArgument.stack:
        return "stack";

   case ExceptionArgument.capacity:
        return "capacity";

   case ExceptionArgument.index:
        return "index";

   case ExceptionArgument.startIndex:
         return "startIndex";

   case ExceptionArgument.value:
        return "value";

   case ExceptionArgument.count:
        return "count";

   case ExceptionArgument.arrayIndex:
        return "arrayIndex";

   case ExceptionArgument.name:
        return "name";

   case ExceptionArgument.mode:
        return "mode";
  }
  return string.Empty;
}

Ok, now you're thinking that the above function could be easily rewritten as:

internal static string GetArgumentName
                          (ExceptionArgument argument)
{
  return argument.ToString();
}

And perhaps the whole shebang of going through three different method calls could be avoided by simply writting:

public virtual void GetObjectData
                       (SerializationInfo info, 
                        StreamingContext context)
{
  if (info == null)
  {
     throw new ArgumentNullException("info");
  }
  // ...
}

Right?!

Well that's what I thought... Until I read the source code.

Thanks to Microsoft the source code for the .NET Framework is public, so as I always like to learn, I've downloaded the full source and installed.

When I read the source for the ThrowHelper class, I've come across the following explanation:

The old way to throw an exception generates quite a lot IL code and assembly code.

Following is an example:

C# source
throw new ArgumentNullException
               ("key", 
                Environment.GetResourceString
                   ("ArgumentNull_Key"));
IL code:
IL_0003: ldstr "key"
IL_0008: ldstr "ArgumentNull_Key"
IL_000d: call string System.Environment::GetResourceString(string)
IL_0012: newobj instance void System.
                                 ArgumentNullException::
                                    .ctor(string,string)
IL_0017: throw

Which is 21bytes in IL.

So we want to get rid of the ldstr and call to Environment.GetResource in IL.

In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the argument name and resource name in a small integer. The source code will be changed to:

ThrowHelper.ThrowArgumentNullException 
    (ExceptionArgument.key, 
     ExceptionResource.ArgumentNull_Key);

The IL code will be 7 bytes.

IL_0008: ldc.i4.4
IL_0009: ldc.i4.4
IL_000a: call void System.ThrowHelper:: 
                      ThrowArgumentNullException 
                         (valuetype System.ExceptionArgument)
IL_000f: ldarg.0

This will also reduce the Jitted code size a lot.

After reading this I started having a little bit more respect for the guys that write this stuff.

Comments