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
StringBuilder
if 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
FormattableString
whenever there is an overload for the string parameter.
So the example above would write STRING: a test 42
to 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 StringBuilder
.Roslyn to the rescue
Luckily we can use Roslyn to do some metaprogramming magic and work around this. Basically we need to rewriteStringBuilder.Append/AppendLine
calls to StringBuilder.AppendFormat
:... 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
SyntaxTree
in a CSharpCompilation
: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.
Conclusion
As you've seen by yourself, it's not particulairly difficult to optimize the back and forth between string interpolation andStringBuilder
. 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