Poking the C# Compiler's Overload Resolution for String and FormattableString

17 Jan 2016

Watching Rob Conery’s Exploring C# 6 with Jon Skeet chapter on Strings, especialy the last part of the chapter “Skeet Creates an ORM” gave me some ideas on possible usages of FormattableString.

I went ahead and poked the compiler to see how it reacted, and the results are somewhat surprising.

A Methods Expecting a FormattableString

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         Print("Hello");
 6     }
 7 
 8     static void Print(FormattableString fs)
 9     {
10         Console.WriteLine(fs.ToString());
11     }
12 }

This doesn’t even compile; the compiler is not willing to convert a String to a FormattableString for you.

Adding a $ in front for the string does the trick, though:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         Print($"Hello");
 6     }
 7 
 8     static void Print(FormattableString fs)
 9     {
10         Console.WriteLine(fs.ToString());
11     }
12 }

The interesting bits are only revealed when looking at what the compiler generated for us (I use ILSpy):

 1 using System;
 2 using System.Runtime.CompilerServices;
 3 
 4 internal class Program
 5 {
 6 	private static void Main(string[] args)
 7 	{
 8 		Program.Print(FormattableStringFactory.Create("Hello", Array.Empty<object>()));
 9 	}
10 
11 	private static void Print(FormattableString fs)
12 	{
13 		Console.WriteLine(fs.ToString());
14 	}
15 }

It’s interesting to look at the whole code, because as pointed out in the Pluralsight course, the compiler looks for a FormattableStringFactory.Create static method in the System.Runtime.CompilerServices. This is how FormattableString can be back ported in earlier version than .NET 4.6.

An Overload that Takes a String

Let’s see what happens when there is an overload that takes a String:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         Print($"Hello");
 6     }
 7 
 8     static void Print(FormattableString fs)
 9     {
10         Console.WriteLine("Print(FormattableString)");
11     }
12 
13     static void Print(string s)
14     {
15         Console.WriteLine("Print(string)");
16     }
17 }

Surprisingly, this prints:

Print(string)

Looking at the generated code once more reveals that the compiler simply removed the $ that was in front of our string.

 1 using System;
 2 
 3 internal class Program
 4 {
 5 	private static void Main(string[] args)
 6 	{
 7 		Program.Print("Hello");
 8 	}
 9 
10 	private static void Print(FormattableString fs)
11 	{
12 		Console.WriteLine("Print(FormattableString)");
13 	}
14 
15 	private static void Print(string s)
16 	{
17 		Console.WriteLine("Print(string)");
18 	}
19 }

So, it seems that we should us a real interpolated string to force the compiler to make a choice:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         Print($"Hello today {DateTime.Today}");
 6     }
 7 
 8     static void Print(FormattableString fs)
 9     {
10         Console.WriteLine("Print(FormattableString)");
11     }
12 
13     static void Print(string s)
14     {
15         Console.WriteLine("Print(string)");
16     }
17 }

Unfortunately, this still prints:

Print(string)

Looking at the generated code reveals that the compiler seems to prefer calling String.Format on out interpolated string when there is an overload that takes a String parameter available.

 1 using System;
 2 
 3 internal class Program
 4 {
 5 	private static void Main(string[] args)
 6 	{
 7 		Program.Print(string.Format("Hello today {0}", DateTime.Today));
 8 	}
 9 
10 	private static void Print(FormattableString fs)
11 	{
12 		Console.WriteLine("Print(FormattableString)");
13 	}
14 
15 	private static void Print(string s)
16 	{
17 		Console.WriteLine("Print(string)");
18 	}
19 }

Forcing the Compiler to Choose the Overload that Takes a FormattableString?

Now, would there be a way to force the compiler into calling the FormattableString overload instead of the String overload? That would be quite nice because it would then allow us to process the FormattableString further before it is rendered as a String; while still keeping a String overload for those that simply want to send a String.

My first naïve attempt was to change the signature of the String version and add params object[] to try to fool the compiler to call the FormattableString overload as it has fewer arguments:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         Print($"Hello today {DateTime.Today}");
 6     }
 7 
 8     static void Print(FormattableString fs)
 9     {
10         Console.WriteLine("Print(FormattableString)");
11     }
12 
13     static void Print(string s, params object[] args)
14     {
15         Console.WriteLine("Print(string)");
16     }
17 }

But this is a failure, the String overload is still being called, and the compiler even goes as far as to add an empty array of objects in the call:

1 Program.Print(string.Format("Hello today {0}", DateTime.Today), Array.Empty<object>());

Making the String Overload an Extension Method

What if the String overload was an extension method? In essence, I’m trying to make the String overload more distant, a less likable choice to the compiler, if you will. We know from previous versions of the C# language specifications (C# 6.0 specs not being out yet) that an extension method are less likely to be picked.

We have to change our code a bit to make it all work on instances, but if that works it’ll be worth it:

 1 class Program
 2 {
 3     static void Main(string[] args)
 4     {
 5         var p = new Program();
 6         p.Print($"Hello today {DateTime.Today}");
 7     }
 8 
 9     void Print(FormattableString fs)
10     {
11         Console.WriteLine("Print(FormattableString)");
12     }
13 }
14 
15 static class ProgramExtensions
16 {
17     public static void Print(this Program p, string s)
18     {
19         Console.WriteLine("Print(string)");
20     }
21 }

Lo and behold, this finally prints what we want:

Print(FormattableString)

The compiler is finally cornered into choosing the FormattableString version over the String version. Admittedly, it is a bit ugly, but it does the trick and can be put to good use in certain cases.


Blog Move and (maybe) revival

05 Jun 2015

It’s been a while since I left this blog unattended, it was time I did something about it.

I decided to do two changes:

  • Host the blog in GitHub pages instead of my own hosting provider
  • Follow the late trend of migrating to Jeckyll/Bootstrap

Let’s see how it goes, and I hope to be blogging a bit more often.


Code Kata: Kata Two

27 Aug 2013

I haven’t been coding much at work recently, so I decided that I need to sharpen the saw a bit and exercise.

I decided to start with some Code Kata. The first one is somewhat an academical problem: implement binary search in five different ways.

Here is my first solution, the iterative way:

        //Classic iterative method
        public int Chop1(int element, IEnumerable<int> collection)
        {
            //Empty collection -> not found
            if (collection.Count() == 0) return -1;

            var lower = 0;
            var upper = collection.Count() - 1;
            int index, candidate;

            while (lower != upper)
            {
                index = lower + ((upper - lower) / 2);
                candidate = collection.ElementAt(index);

                if (candidate == element)
                {
                    return index;
                }
                else if (candidate < element)
                {
                    lower = index + 1;
                }
                else //if (candidate > element)
                {
                    upper = index;
                }
            }

            return (collection.ElementAt(upper) == element) ? upper : -1;
        }

I was actually scared at the time it took me. This is the kind of problem you are expected to solve in 5 minutes flat at university, but the lack of programming puzzles lately soften my brain. On the bright side, I challenged myself to get it right without compiling before being absolutely sure that the code would pass the tests, which it did. This involved some scratching paper to test the different corner cases, but I felt good when the green light of passing tests appeared.

Here is my second solution, recursive and passing slices of the original array:

        //Recursively passing smaller slices of the initial array
        public int Chop2(int element, IEnumerable<int> collection)
        {
            //Empty collection -> not found
            if (collection.Count() == 0) return -1;

            var index = collection.Count() / 2;
            var candidate = collection.ElementAt(index);

            if (candidate == element)
            {
                return index;
            }
            else if (candidate < element)
            {
                var offset = index + 1;
                var position = Chop2(element, collection.Skip(index + 1));
                return position > -1 ? offset + position : -1;
            }
            else //if (candidate > element)
            {
                return Chop2(element, collection.Take(index));
            }
        }

After finishing this one, I started thinking about it and found it very funny that this came more naturally than a recursive function using boundaries inside the array. I tend to use LINQ a lot when I code in C#, and I guess this is where it shows. The good news is that this led me to the third possible solution, which would have been second if I was just out of university.

Third solution, recursive and passing arrays boundaries:

        //Classic recursive method, passing boundaries around
        public int Chop3(int element, IEnumerable<int> collection)
        {
            //Empty collection -> not found
            if (collection.Count() == 0) return -1;

            return SubChop3(element, collection, 0, collection.Count() - 1);
        }
        //...and its sub method required for passing boundaries around
        private int SubChop3(int element, IEnumerable<int> collection, int lower, int upper)
        {
            //End case: lower == upper
            if (lower == upper)
            {
                if (collection.ElementAt(lower) == element) return lower;
                else return -1;
            }

            var index = lower + ((upper - lower) / 2);
            var candidate = collection.ElementAt(index);

            if (candidate == element)
            {
                return index;
            }
            else if (candidate < element)
            {
                return SubChop3(element, collection, index + 1, upper);
            }
            else //if (candidate > element)
            {
                return SubChop3(element, collection, lower, index);
            }
        }

Nothing too fancy here.

I don’t have a 4 and 5 yet, but I already have one idea that is very challenging: continuation passing style. I was also tempted to implement one version using TPL, but it wouldn’t be very different from the recursive methods, so I am not sure… Maybe using a custom partitioner?


A Tale of Code Review…

07 Aug 2012

A few years ago, I was working on a project where our client asked for code review by experts from another company. The idea behind this was to assess that we were doing a good job, fair enough. Since they didn’t have any in-house experts on the matter, the only choice left was to hire experts on the topic to review our work.

I don’t remember all the details of this code review, but I have two remarks that distinctly remember because they shocked me.

No Need to Have a Static Private Object to Lock On, Just Lock the Type

Now when I was told that, I was hit very hard because I was certain that the language specifications specifically mentioned that as a bad practice. Turns out that I remembered well: http://msdn.microsoft.com/en-us/library/c5kehkcz.

The good thing is that this one, I managed to avoid having to change it in the code. I don’t remember very well the why, but it was a small victory.

Don’t Use the “+” Operator on String, Use String.Concat

This one was also a good one. When I was told that I lost all hopes that these experts would be useful, as this comment revealed their ignorance of the C# compiler behavior. Not that I think everyone should know that, but I don’t consider myself as a C# guru, but I believe that any enthusiast that played with reflector a little bit should have noticed that.

Not only the “+” operator is replaced by String.Concat call if the content of both operand is not known at compile time, but if both are known at compile time then it is simply replaced by one single String! So, applying this guideline did reduce performances… Not to a noticeable way, I agree, but having to go in your code and replace good stuff with something not as good is not a pleasant experience.

The “fun” part is that actually we also had the String.Concat comment for the “+=” operator. So, one of my colleague  went through the code and replaced all the

s1 += s2;

by

String.Concat(s1, s2);

which is wrong since the return value is ignored, so original string is not modified. Not only this was a pointless change, but bugs were introduced when doing that mindless task.

Conclusion

I don’t know if there is anything to be concluded from this experience. In my case, the advices provided were mostly useless if not harmful. My personal conclusion is that real experts are a rare breed, and it’s not because someone labeled as an expert and expensive that he is. Even if he comes from a big corporation…

Now there is also a conflict of interest here. When an expert is dispatched on this kind of mission, it would be very difficult to justify his price if he didn’t have anything to comment on. So whatever you have done, the expert will have comments and if quality is good chances are that these comments will be useless. Next time, I’ll put some ducks here and there. Needless to say, if the customer is clueless about the technology, he will take the side of the expert. Which is perfectly understandable, because for that particular manager, not following the expert’s advice can only result in a lot of pain if something happens, even if completely unrelated to the expert’s comments. Another example of C.Y.A.

My third conclusion what that I probably was expecting too much from these experts. To me, the word “expert” means people like those on the top reputation page of StackOverflow.


Diablo III

19 Oct 2011

I have been playing the beta for a while now and maxed out a barbarian.

Diablo002

I have to say that it’s still Diablo, nothing changed much compared to the first two. But that’s why I like it I guess!

Plus the little exclusivity of playing the closed Beta, too!