Tuesday, 4 December 2012

Dynamic Typing and Extension Methods

Earlier today I came across an interesting runtime exception of type RuntimeBinderException. The specific exception message was:

'System.DateTime' does not contain a definition for 'ToUKFormat'

'ToUKFormat' is a custom extension method on DateTime that I wrote and was in-scope at compile time when I called it. So what caused the runtime exception? It turns out that you cannot call an extension method on an object/value that is dynamically typed. In my case, I was using the dynamic ViewBag in an ASP.NET MVC application to store a DateTime value - then in my view, I was retrieving that value from the ViewBag and calling a custom written extension method on it ('ToUKFormat'). In razor syntax, I was doing something similar to:

@ViewBag.Foo.Bar.ToUKFormat()
I have given the solution further below but if you want to reproduce the problem in a simple console application, you can use the following code:

namespace DynamicRuntimeIssue
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic today = DateTime.Today;

            Console.WriteLine(today.ToUKFormat()); // Exception...
            Console.ReadLine();
        }
    }

    static class DateTimeExtensions
    {
        public static string ToUKFormat(this DateTime dateTime)
        {
            return dateTime.ToString("dd/MM/yyyy");
        }
    }
}
When you run the code above, you will encounter the aforementioned exception. Of course, if you appropriately type the 'today' variable to a DateTime rather than dynamic, then you'll find that it all works as expected. However, if you cannot avoid the use of dynamic, like for example in my case where I was using ViewBag - then you can overcome this exception by calling your extension method as if it was any normal static method. You call the method and pass-in your object/value through as an actual parameter. Following on from the example above, you would therefore have:

Console.WriteLine(DateTimeExtensions.ToUKFormat(today));
Rather than:

Console.WriteLine(today.ToUKFormat());
If you want to understand why you can't call extension methods on a dynamic type, then go over to this stackoverflow question where someone else has had the same issue - you will particularly want to read Eric Lippert's response dated March 15 2011.