StringComparison.InvariantCulture is not always invariant
CultureInfo.InvariantCulture
is not invariant for all operations. It is a special culture that is used for formatting and parsing operations that do not depend on any specific culture. For instance, this is well-suited to format values in order to persist them. However, it is not invariant for string comparisons. Comparing strings using InvariantCulture
can lead to different results depending on the current version of NLS or ICU used by the .NET runtime.
If you look at the code of .NET, only the CultureData
is invariant: https://github.com/dotnet/runtime/blob/64252fba0225d92164875824a70b40cc86b7b063/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs#L546-L655.
// Invariant (formatting / parsing)
var value = -42;
_ = value.ToString("C", CultureInfo.InvariantCulture);
_ = int.Parse("-42", CultureInfo.InvariantCulture);
// Not invariant (comparisons, may produce different results)
string a = "...";
string b = "...";
_ = string.Equals(a, b, StringComparison.InvariantCulture);
_ = StringComparer.InvariantCulture.Compare(a, b);
StringComparison.InvariantCulture
or StringComparer.InvariantCulture
usages are wrong most of the time and should be replaced with Ordinal
comparisons. In my coding standards, the 2 symbols are banned using Microsoft.CodeAnalysis.BannedApiAnalyzers
and the following configuration:
F:System.StringComparison.InvariantCulture;Do you mean Ordinal?
F:System.StringComparison.InvariantCultureIgnoreCase;Do you mean OrdinalIgnoreCase?
P:System.StringComparer.InvariantCulture;Do you mean Ordinal?
P:System.StringComparer.InvariantCultureIgnoreCase;Do you mean OrdinalIgnoreCase?
Note that you can use App-local ICU to fix the version of ICU and get consistent behavior across different environments. This is especially useful when you need to ensure that your application behaves the same way regardless of the system's locale settings.
Another way is to use the Globalization Invariant Mode, which is a mode that allows you to run .NET applications without relying on the underlying operating system's globalization features. This can be useful in scenarios where you want to ensure consistent behavior across different platforms or when you want to reduce the size of your application by not including the full globalization data. Note that this mode is not equivalent to using Ordinal
when comparing strings.
#Additional resources
Do you have a question or a suggestion about this post? Contact me!