Ошибка в сравнении строк .NET Framework

Это требование для любого сортировки сортировки для работы, что основной оператор порядка транзитивный и антисимметричный.

В .NET это неверно для некоторых строк:

static void CompareBug()
{
  string x = "\u002D\u30A2";  // or just "-ア" if charset allows
  string y = "\u3042";        // or just "あ" if charset allows

  Console.WriteLine(x.CompareTo(y));  // positive one
  Console.WriteLine(y.CompareTo(x));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(x, y));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(y, x));  // positive one

  var ja = StringComparer.Create(new CultureInfo("ja-JP", false), false);
  Console.WriteLine(ja.Compare(x, y));  // positive one
  Console.WriteLine(ja.Compare(y, x));  // positive one
}

Вы видите, что x строго больше y, а y строго больше x.

Потому что x.CompareTo(x) и т.д. все дают нуль (0), ясно, что это не порядок. Неудивительно, что я получаю непредсказуемые результаты, когда я Sort массивы или списки, содержащие строки типа x и y. Хотя я не тестировал это, я уверен, что SortedDictionary<string, WhatEver> будет иметь проблемы, сохраняя себя в отсортированном порядке и/или размещая элементы, если для ключей используются строки типа x и y.

Известна ли эта ошибка? Какие версии фреймворка затронуты (я пытаюсь это сделать с .NET 4.0)?

EDIT:

Здесь пример, где знак отрицателен в любом случае:

x = "\u4E00\u30A0";         // equiv: "一゠"
y = "\u4E00\u002D\u0041";   // equiv: "一-A"

Ответ 1

Я столкнулся с этим сообщением SO, в то время как я пытался выяснить, почему у меня возникли проблемы с извлечением (строковыми) ключами, которые были вставлены в SortedList, после того как я обнаружил, что причиной было нечетное поведение .Net 40 и выше (a1 < a2 и a2 < a3, но a1 > a3).

Моя борьба за то, что происходит, можно найти здесь: С# SortedList < string, TValue > .ContainsKey для успешно добавленного ключа возвращает false.

Возможно, вы захотите посмотреть раздел "ОБНОВЛЕНИЕ 3" моего вопроса SO. Похоже, что проблема была отправлена ​​Microsoft в декабре 2012 года и закрыта до конца января 2013 года, поскольку "не будет исправлена". Кроме того, в нем приведено обходное решение, которое может быть использовано.

Я создал реализацию этого рекомендуемого обходного пути и проверил, что он исправил проблему, с которой я столкнулся. Я также просто подтвердил, что это устраняет проблему, о которой вы сообщали.

public static void SO_13254153_Question()
{
    string x = "\u002D\u30A2";  // or just "-ア" if charset allows
    string y = "\u3042";        // or just "あ" if charset allows        

    var invariantComparer = new WorkAroundStringComparer();
    var japaneseComparer = new WorkAroundStringComparer(new System.Globalization.CultureInfo("ja-JP", false));
    Console.WriteLine(x.CompareTo(y));  // positive one
    Console.WriteLine(y.CompareTo(x));  // positive one
    Console.WriteLine(invariantComparer.Compare(x, y));  // negative one
    Console.WriteLine(invariantComparer.Compare(y, x));  // positive one
    Console.WriteLine(japaneseComparer.Compare(x, y));  // negative one
    Console.WriteLine(japaneseComparer.Compare(y, x));  // positive one
}

Оставшаяся проблема заключается в том, что это обходное решение настолько медленное, что практически невозможно использовать с большими наборами строк. Поэтому я надеюсь, что Microsoft передумает закрыть эту проблему или что кто-то знает об улучшении обхода.

Ответ 2

Если правильная сортировка настолько важна в вашей проблеме, просто используйте порядковый сравнение строк вместо чувствительной к культуре. Только это гарантирует переходное и антисимметричное сравнение, которое вы хотите.

Что MSDN говорит:

Указание параметра StringComparison.Ordinal или Значение StringComparison.OrdinalIgnoreCase в вызове метода означает нелингвистическое сравнение, в котором особенности естественных языков игнорируются. Методы, вызываемые с помощью этих StringComparison значения основанные на строковых операционных решениях при сравнении простых байтов вместо таблиц обтекания или эквивалентности, которые параметризуются культура. В большинстве случаев этот подход лучше всего подходит для интерпретация строк, делая код быстрее и надежнее.

И он работает как ожидалось:

    Console.WriteLine(String.Compare(x, y, StringComparison.Ordinal));  // -12309
    Console.WriteLine(String.Compare(y, x, StringComparison.Ordinal));  // 12309

Да, это не объясняет, почему культурно-чувствительное сравнение дает непоследовательные результаты. Ну, странная культура - странный результат.