'string.Compare(String, Int32, String, Int32, Int32) is not compared correctly for Thai Culture in .NET 5.0

In the code snippet below. In the case of Thai Culture, the result should be -1, but it is 0.

Is anyone aware of the reason for this?

public void CountryCulture()
{
    System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");
    string value1 = "#,##0.00";
    string value2 = ";";
    int result = string.Compare(value1, 0, value2, 0, 1);
    Console.WriteLine(result);
}


Solution 1:[1]

Notes:

  • As spender pointed out in this comment, the expected result of the comparison should be -1. not 1.

  • Assuming you've added the line changing the current culture for reproducibility reasons, your code can be condensed to:

    Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");
    var result = string.Compare("#", ";");
    Console.WriteLine(result); // Writes 0 on .NET 5; writes -1 on .NET Framework
    
  • The exact value is irrelevant - only its sign; the documentation for the String.Compare method says:

    All overloads of the Compare method return a 32-bit signed integer indicating the lexical relationship between the two comparands.

    Value Condition
    Less than zero The first substring precedes the second substring in the sort order.
    Zero The substrings occur in the same position in the sort order, or length is zero.
    Greater than zero The first substring follows the second substring in the sort order.

If you want an expected (i.e. negative) value, there are a few options available:

  1. Use an overload which takes a StringComparison, e.g. Compare(String, String, StringComparison):

    Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");
    var result = string.Compare("#", ";", StringComparison.Ordinal);
    Console.WriteLine(result); // Writes "-24" on .NET 5 and .NET Framework
    
  2. Use a member of the String.CompareOrdinal overload group, e.g. CompareOrdinal(String, String):

    Thread.CurrentThread.CurrentCulture = new CultureInfo("th-TH");
    var result = string.CompareOrdinal("#", ";");
    Console.WriteLine(result); // Writes "-24" on .NET 5 and .NET Framework
    

As for your question

Is anyone aware of the reason for this?

Hans Passant effectively provided it in his link to .NET globalization and ICU in this comment.

Quoting a relevant part of the document (emphasis mine):

Prior to .NET 5, the .NET globalization APIs used different underlying libraries on different platforms. On Unix, the APIs used International Components for Unicode (ICU), and on Windows, they used National Language Support (NLS). This resulted in some behavioral differences in a handful of globalization APIs when running applications on different platforms. Behavior differences were evident in these areas:

  • Cultures and culture data
  • String casing
  • String sorting and searching
  • Sort keys
  • String normalization
  • Internationalized Domain Names (IDN) support
  • Time zone display name on Linux

Starting with .NET 5, developers have more control over which underlying library is used, enabling applications to avoid differences across platforms.

...

If you upgrade your app to target .NET 5, you might see changes in your app even if you don't realize you're using globalization facilities.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Wai Ha Lee