C# String Interpolation

Dan Liberatore
CodeX
Published in
7 min readAug 25, 2021

You’re a C# programmer in 2021. You need to build strings that are more complex than a simple ToString() call.

This article will give you a look at many of the ways you’d do that, ending with what I believe is the current best solution.

Note: This article applies to C# in general. It also applies to Visual Basic in the .NET era, but converting between the two is up to you. The Debug.Log you see sprinkled throughout this article is Unity Engine’s Debug.Log. You might be using plain dotnet instead — in that case, your logging might happen with Console.WriteLine, System.Diagnostics.Debug.WriteLine or something more exotic in your undoubtedly slick ASP.NET API.

Let’s say you have these three values and you need to print them on one line.

When you need to get those values into a string like “values: 3, 1, 2”…

String Concatenation

Ah, the classic.

Do you suffer from code like this?

String concatenation, short form

Or like this?

String concatenation, long form

It seems so quick and easy, doesn’t it?

There’s a significant catch, besides just the readability and cluttered syntax.

Concatenating two strings requires a memory allocation and two copy operations. That code snippet in the picture? Think about how many allocations, copy operations, and subsequent garbage collections will be required. That’s new memory allocations and copy operations for each concatenation, plus eventual garbage collection, and that adds up fast! To the heap with that.

This style of string building harkens back to BASIC in the 1980s, back when we’d type programs in line-by-line on our amber screens and hope we didn’t make a typo. If you’re also from that era, let’s take a moment together to realize that was 40 years ago, before we go back to yelling at the kids on our lawn.

Yes, I know Fortran 77 had string concatenation before that, and I’m sure someone could find an even older one on Rosetta Code. The point being, it’s probably time for an update. There have been a few innovations since then.

StringBuilder

Let’s check out some more efficient tech from the ‘90s and early ‘00s.

You’ve heard StringBuilder is good (it IS) but maybe it seems like overkill for just putting a couple values together.

StringBuilder being used nicely — and VS Code reminding me that there can be improvements

C# got ahold of this with .NET Framework 1.1, in 2002.

Some of you recovering Java coders will recognize StringBuilder from Java 1.5 in 2004 or — if you have a few more gray hairs — StringBuffer from the original Java in 1998.

C++ has had a class in the Standard Library called std::stringstream since C++98. It remains notable enough to have a minimal Wikipedia article.

StringBuilder does most of its memory allocation on initialization, and when you call ToString(). Otherwise, it primarily copies a value once to already-claimed memory when you call Append, expanding with additional ‘chunk’ allocations as needed. Check out the source, if you’re into that sort of thing.

Composite Formatting

Maybe you’ve tried composite formatting via String.Format.

Composite formatting — now we’re really peeking our head out of the 1990s
More compact version

Composite formatting was really nice when it came out. It gave us template-style string building as an official language feature. It felt clunky when you used it for anything non-trivial, where it resulted in some extremely long lines of code, but it was there and you didn’t have to write or find a string templating library. And you could use formatting strings, the same as you’d use in String.Format!

Is the SHL/SHR circuit in a CPU a… shift worker?

It’s nice until you need to change what you’re doing. For example, if you need to reorder the values, or insert new values into the middle of the string. Now you’re heading towards inadvertently creating bugs in your APIs. Juggling those lists of arguments gets to be a noticeable mental load.

By the way, String.Format uses StringBuilder under the hood.

String.Join

You’ve probably heard of using String.Join to convert lists of objects into strings, too. It’s an elegant tool for specific use cases. It’s been around just as long as StringBuilder.

Originally, you could join string arrays, and that was it.

People all over the world, join strings

People wrote some clever custom workarounds to support joining other types.

String.Join received support for joining generic objects together in 2010, with .NET Framework 4.0. (Unity wouldn’t get support for this until 2017.)

People all over the world, join things

String.Join does its own unique thing under the hood.

String Interpolation

Now that we’ve seen all the old ways to do it, let’s look at something less than 10 years old as of this writing in 2021.

String interpolation has been around for a while now. How long? It was introduced in 2014 by C# 6 and this was the version aligned with .NET Framework 4.6 / CLR 4, with limited support and caveats for earlier versions of .NET Framework. It’s been supported natively from Visual Studio 2015, Visual Studio 2013 via an addon, and Unity 2017. Back then, Unity called their C# 6 support in 2017 “experimental” and you had to manually select .NET 4.6 for your project in order to use it.

Yeah, yeah. Boring history lessons, am I right? The point is, it’s been around long enough that it should be in more common use.

How would you like your string building to look like this instead?

String interpolation two ways: the new hotness

This is called string interpolation. The astute reader may say “Hey, that looks like templates from other languages.” And you would basically be right!

It’s not a simple wrapper around a specific string function, though. There’s a nice article about what the compiler does behind the scenes but the beauty of it is that you don’t need to care how it works.

I’ll summarize and give a couple more real-world examples. There’s a nice tutorial in the documentation. There’s also a reference with ALL the gory details.

To tell the compiler that you want interpolation on a string, simply prefix your opening double-quote character with a $ character. It’s important that there is nothing between the two characters. Well, except for maybe an @ in C# 8 and later, because then you can do interpolated verbatim strings as well.

In an interpolated string, you can name variables and properties in between curly braces like {myVal} and your code becomes much more readable. If you need a curly brace literal in your string, you will have to double it as a sort of escaping mechanism, because single curly braces now have special meaning.

You’re not limited to only variables or properties. You can put any value expression inside the curly braces. Function calls, mathematical expressions, you name it — as long as it has a value, it can go there. You can even do formatting like you would with String.Format.

Sometimes you only have the appetite for a small piece of pi

It will convert objects using their default ToString() methods, so make sure those are in place. If you need to get a value via code, for example with a ternary operator, you can do that too… it’s not super readable, but go right ahead. Please drop a comment in your code for the next person/you who has to maintain it later.

Can you write good, concise code? Of course you can. You’re an expert.

When to Avoid

This is a huge boon to debugging and formatting, and it helps us out with the first and arguably most important virtue of a programmer: laziness.

It may be tempting, but never, EVER, use string interpolation to handle un-sanitized user input for the purpose of parameterizing SQL queries, assembling command lines or API calls, or anything else that truly requires sanitization. A user with intent could easily cause buffer overflows with this.

This advice is true of any string manipulation but it bears repeating: Always sanitize user input.

Performance

I hear you asking, “What about performance?”

It’s fast enough that if I wanted to make anything faster, I’d be on the wrong side of diminishing returns for my game dev and data processing purposes. It’s as good as StringBuilder.

You want benchmarks? Objective evidence? Excellent. Please check out:

Loggers

There are some alternatives if you’re using 3rd party loggers. Serilog comes to mind right away. If you’re building an ASP.Net application, you should probably be using it with its enrichers, if not something similar.

Thanks for reading!

--

--