StringComparison.InvariantCulture is not always invariant

 
 
  • Gérald Barré

CultureInfo.InvariantCulture is not invariant for all operations. It is a special culture designed for formatting and parsing operations that are culture-independent, making it well-suited for persisting values. However, it is not invariant for string comparisons. Comparing strings using InvariantCulture can produce different results depending on the 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.

C#
// 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 and StringComparer.InvariantCulture are incorrect in most situations and should be replaced with Ordinal comparisons. In my coding standards, these two symbols are banned using Microsoft.CodeAnalysis.BannedApiAnalyzers with the following configuration:

BannedSymbols.txt
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?

You can use App-local ICU to pin the ICU version and ensure consistent behavior across environments, regardless of the system's locale settings.

Alternatively, Globalization Invariant Mode lets you run .NET applications without relying on the operating system's globalization features. This is useful for ensuring consistent behavior across platforms or reducing application size by omitting full globalization data. Note that this mode is not equivalent to using Ordinal for string comparisons.

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?