Using proper culture with C# string interpolation

Visual Studio General .NET Core

4 years ago

Once you get used to string interpolation feature in C# it is easy to get addicted to it. However, we need to keep in mind the fact that the current system culture matters. Consider we are building a code that communicates with JavaScript – for example in Web Assembly – and needs to execute a function that takes in a decimal number as an argument:

As simple as our sample looks, it contains a remarkable bug. Why? String interpolation was built as an alternative to the string.Format method. In fact, in the generated IL we can see it actually evaluates to string.Format call:

The output of string.Format depends on the CultureInfo.CurrentCulture and so does string interpolation's output. Hence, when you run this code on a PC with the en-US (American English) culture, it evaluates to:

But running on a PC with cs-CZ (Czech) culture we suddenly get:

The decimal comma would certainly be appropriate when displayed to the user. In our case, however, this would mean a call to the myFunction function with two arguments – 1 and 2! We could use string.Format directly to solve this problem, as it allows us to specify a IFormatProvider to control formatting. But can we keep using an interpolated string and evaluate it to a machine-friendly representation?

Solution

As I have mentioned, interpolated string in most cases evaluates to a plain old string.Format call. However, when needed, the syntax can evaluate to an instance of System.FormattableString. This helpful class essentially wraps the interpolated string and gives read-only access to the format string, its arguments, and it also offers a static Invariant method. This method takes a single FormattableString argument (your interpolated string) and evaluates it according to the Invariant culture (CultureInfo.InvariantCulture):

That is ideal, as invariant culture always stays the same, so we can rest assured that the interpolated string evaluation will always result in:

Tip: FormattableString.Invariant is a mouthful. If you use it often, you can utilize the using static feature, which was introduced in C# 6 and then reference the method directly as Invariant:

Specifying culture

You might be wondering now if there is a method that would allow us to use a specific culture to use when evaluating an interpolated string. The first attempt might be to just do the following:

Unfortunately, this does not work as we expect. Because we are not explicitly casting the interpolated string to FormattableString, it gets directly evaluated to string.Format, so we are then just calling ToString(usCulture) on the resulting string. And string.ToString implementations are in fact just no-ops:

So we need to do a small side-step instead:

By explicitly storing the interpolated string in a FormattableString variable, we can now call the FormattableString.ToString(IFormatProvider) implementation, which actually does what we need - evaluates the interpolation under the given culture.

Source code

Example source code for this blog post is available on my GitHub. It showcases all discussed options in a single .NET Core 3.0 console app:

Invariant culture sample

Invariant culture sample

Summary

Interpolated strings in C# are handy, but we have to ensure that they are evaluated correctly according to the context where the resulting string is presented. If we are displaying it to the user, the default behavior of using CultureInfo.CurrentCulture is probably most appropriate. On the other hand, in machine-to-machine communication, we should always make sure to use invariant culture using FormattableString.Invariant.