Thursday, May 28, 2009

The Dynamic Type and Runtime Overload Resolution

I've written about the upcoming dynamic keyword in C# a little bit already, but did you know that the dynamic type enables you to perform runtime resolution of method overloads?

In the past, the specific overload of a method to be called was statically defined at compile time within a generic method, and the least specific overload was selected as the method to be called at runtime. This all happened at compile time. Take the following example:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomethingGeneric("Howdy!");
DoSomethingGeneric(1);
}

static void DoSomethingGeneric<T>(T value)
{
DoSomething(value);
}

static void DoSomething(object value)
{
Console.WriteLine("Object! {0}", value.GetType());
}

static void DoSomething(int value)
{
Console.WriteLine("Int!");
}

static void DoSomething(string value)
{
Console.WriteLine("String!");
}
}
}


The output of this method is the following:

Object! System.String
Object! System.Int32

Notice that regardless of the type you send in to DoSomethingGeneric, it's the object version that gets called. This is because there is no way for the C# 3.0 compiler to determine that the intention of the user is to call the integer or string versions of the overload at compile time, so the compiler opts for the most non-specific version of the overload, the object version, and compiles the call to the object version into the IL of the method.

This all changes now with the dynamic type. The above code will still work the same, but take the following code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomethingDynamic("Howdy!");
DoSomethingDynamic(1);
DoSomethingDynamic(DateTime.Now);
}

static void DoSomethingDynamic(dynamic value)
{
DoSomething(value);
}

static void DoSomething(object value)
{
Console.WriteLine("Object! {0}", value.GetType());
}

static void DoSomething(int value)
{
Console.WriteLine("Int!");
}

static void DoSomething(string value)
{
Console.WriteLine("String!");
}
}
}


This is a bit different. The output of this code is the following:

String!
Int!
Object! System.DateTime

So the dynamic type can help in runtime method overload resolution!

That being said, there is a slight performance penalty to using the dynamic object, but it wasn't quite as much as I expected. Here's the code I used to test the performance. Just for kicks, I threw in a reflection version as well, to see how fast that one returned...


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
// performance tests.
Console.Write("Milliseconds to complete one million iterations of the generic method: ");
Test(() => DoSomethingGeneric("Howdy"), 10000000);
Console.Write("Milliseconds to complete one million iterations of the dynamic method: ");
Test(() => DoSomethingDynamic("Howdy"), 10000000);
Console.Write("Milliseconds to complete one million iterations of the reflection method: ");
Test(() => DoSomethingReflective("Howdy"), 10000000);
}

static void Test(Action action, int iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
action();
Console.WriteLine(sw.ElapsedMilliseconds);
}

static void DoSomethingGeneric<T>(T value)
{
DoSomething(value);
}

static void DoSomethingDynamic(dynamic value)
{
DoSomething(value);
}

static void DoSomethingReflective<T>(T value)
{
MethodInfo info = typeof(Program).GetMethod("DoSomething", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(T) }, null);
if (info == null)
DoSomething(value);
else
info.Invoke(null, new object[] { value });
}

static void DoSomething(object value) { }

static void DoSomething(int value) { }

static void DoSomething(string value) { }
}
}


The output I got from this is the following:

Milliseconds to complete one million iterations of the generic method: 136
Milliseconds to complete one million iterations of the dynamic method: 2589
Milliseconds to complete one million iterations of the reflection method: 102552

Needless to say, dynamic is much faster than reflection, though slightly slower than the generic version... which doesn't call the proper overload anyways.