Currently Roslyn emits the following IL for this call:
You see what it does there? Let's translate that back to C#, to make it more obvious:
It allocates another string and possibly even another StringBuilder. The thing is, you wouldn't be using a
StringBuilderif you weren't concerned about allocations. My initial idea of how to solve this was
FormattableString. So basically something like this:
Unfortunately overload resolution doesn't work in favor of the method accepting the
FormattableStringwhenever there is an overload for the string parameter. So the example above would write
STRING: a test 42to the console. If we could make the overload resolution smarter (e.g. make the compiler create FormattableString instances wherever there's an matching overload accepting FormattableString instead of a string argument) the solution would be as easy as creating
Append/AppendLine(FormattableString)extension methods for
Roslyn to the rescueLuckily we can use Roslyn to do some metaprogramming magic and work around this. Basically we need to rewrite
... in IL speak:
Let's create a reusable Roslyn-based solution that knows how to do that optimization.
We can use the class above to rewrite each
Originally I wanted the optimization to do the same for TextWriter.Write/WriteLine and Console.Write/WriteLine calls, but it turns out that they actually call string.Format internally anyway. So, add that to the list of possible optimizations.
ConclusionAs you've seen by yourself, it's not particulairly difficult to optimize the back and forth between string interpolation and
StringBuilder. I really think optimizations like that should be in Roslyn. As long as that's not implemented though, using string interpolation to build big string fragments (like for example HTML...) might be a bit more expensive than you might think.
Stay tuned for my next blog post, where I'll show you how to plug the optimization into the metaprogramming infrastructure of DNX and/or StackExchange.Precompilation, if you're not ready to migrate to vNext yet