Overload Resolution Oops

Earlier today I was observing the output of some calls to Debug.WriteLine when I decided that one of the messages was a little too verbose. Basically the message included a fully qualified name when just the class name would suffice. I found the line which originally read:

Debug.WriteLine("Storing Pending Events for {0}", aggregate.GetType());

and changed it to:

Debug.WriteLine("Storing Pending Events for {0}", aggregate.GetType().Name);

Upon rerunning the app I saw something that surprised me but really shouldn’t have. The message now read “Document: Storing Pending Events for {0}” instead of “Storing Pending Events for Document.” How could this be?

The issue came down to overload resolution. Debug.WriteLine has five overloads but we’re really only interested in the last two:

public static void WriteLine(Object value)
public static void WriteLine(string message)
public static void WriteLine(Object value, string category)
public static void WriteLine(string format, params Object[] args)
public static void WriteLine(string message, string category)

The final two overloads serve very different purposes but potentially conflict as I was observing. One of them writes a formatted message (using String.Format behind the scenes) whereas the other writes a message and category. It just so happens that changing the second argument from aggregate.GetType() to aggregate.GetType().Name resulted in the compiler selecting a different overload because the one that accepts two strings is a better match than the one that accepts the object params array. Had our message included two or more format arguments we’d have never seen this but because we happened to be passing a Type rather than a string we got the params version.

To resolve the problem I first wrapped the two arguments into a call to String.Format but of course ReSharper complained about a redundant call (apparently it also thought that params version would be called). Ultimately I just cast the name to object and moved on.

Debug.WriteLine("Storing Pending Events for {0}", (object)aggregate.GetType().Name);

Like I said, this really shouldn’t have surprised me but it did. Hopefully next time I’ll remember that there’s a potentially conflicting overload to watch out for.