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:
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
.