Forwarding calls to string.Create with custom culture info.

Hi. I have been trying to create a method to help us to do string interpolation using invariant culture. string interpolation picks up the current thread culture when formatting numbers and such and that can lead to surprises.
// x might be "0.00" but on my machine it is "0,00"
var x = $"{0.00:0.00}";
// x might be "0.00" but on my machine it is "0,00"
var x = $"{0.00:0.00}";
We can use string.Create to specify a culture info such as
// x will always be "0.00"
var x = string.Create(CultureInfo.InvariantCulture, $"{0.00:0.00}");
// x will always be "0.00"
var x = string.Create(CultureInfo.InvariantCulture, $"{0.00:0.00}");
But asking people to type string.Create(CultureInfo.InvariantCulture, whenever they do string interpolation will just be ignored. I thought I could make a little method called Inv that thanks to using static allows me to type:
using static StringInterpolation;
Console.WriteLine(Inv($"{0.00:0.00}"));

static class StringInterpolation
{
public static string Inv(ref DefaultInterpolatedStringHandler handler)
{
return string.Create(CultureInfo.InvariantCulture, ref handler);
}
}
using static StringInterpolation;
Console.WriteLine(Inv($"{0.00:0.00}"));

static class StringInterpolation
{
public static string Inv(ref DefaultInterpolatedStringHandler handler)
{
return string.Create(CultureInfo.InvariantCulture, ref handler);
}
}
The problem is that this won't work for me. TBH I don't really understand why it doesn't work since I am just creating a method around string.Create. It seems it uses the current thread culture and ignores me passing the invariant culture. Looking at string.Create
public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(provider))] ref DefaultInterpolatedStringHandler handler) =>
handler.ToStringAndClear();
public static string Create(IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(provider))] ref DefaultInterpolatedStringHandler handler) =>
handler.ToStringAndClear();
So this method doesn't even use the provider in the implementation. Probably it's related to the attribute [InterpolatedStringHandlerArgument(nameof(provider))] I tried a few options but I hit the post limit so I can't show them. I would like to avoid setting thread culture (global variables in 2024?) or using FormattableString (perf). So I ran out of ideas I thought I check here if someone else has some ideas.
4 Replies
Austin Wise
Austin Wise3mo ago
You could write your own interpolated string handler that always uses invariant culture. Here are some docs: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler
Explore C# string interpolation handlers
This advanced tutorial shows how you can write a custom string interpolation handler that hooks into the runtime processing of an interpolated string.
SleepWellPupper
SleepWellPupper2mo ago
I would like to avoid setting thread culture
Why?
Austin Wise
Austin Wise2mo ago
Here is an example of how to create your own interpolated string handler that uses invariant culture to format values: https://gist.github.com/AustinWise/7464c30f6bc9b531020f00abbf6f9ae4
Gist
Example of a custom c# string interpolation handler that uses invar...
Example of a custom c# string interpolation handler that uses invarient culture to format values - Program.cs
Austin Wise
Austin Wise2mo ago
And to directly address why your Inv function did not worked as you had hoped and why string.Create() does nothing with the provider parameter, it is helpful to see how the compiler transforms calls to string.Create. https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQAwAIpwHQHEA2A9gEYCG+AlgF6kAuFhAdgNzLJQDMmcAbJjOgDC6AN7J0EzFyx8sGALIAKAGZE66ZQEpR4yXqgB2bqmyCATgFM6FxYICu+WncsBJRssLY3AN1JmKpIy09o7OFgA06AAkAETKIKLKAL4xmqxIeknISUA=== You can see the compiler generated code uses the provider before it calls string.Create.
SharpLab
C#/VB/F# compiler playground.
Want results from more Discord servers?
Add your server