Por que o comparador de cadeias padrão falha em manter a consistência transitiva?

Eu conheço esse problemafoi observado antes, de forma mais ou menos concisa, mas ainda assim crio esse novo thread porque me deparei com o problema novamente ao escrever um teste de unidade.

A comparação de cadeias padrão (que é a comparação sensível a maiúsculas e minúsculas dependente da cultura que obtemos comstring.CompareTo(string), Comparer<string>.Default, StringComparer.CurrentCulture, string.Compare(string, string) e outros) viola a transitividade quando as seqüências contêm hífens (ou sinais de menos, estou falando de caracteres simples U + 002D).

Aqui está uma simples reprodução:

static void Main()
{
  const string a = "fk-";
  const string b = "-fk";
  const string c = "Fk";

  Console.WriteLine(a.CompareTo(b));  // "-1"
  Console.WriteLine(b.CompareTo(c));  // "-1"
  Console.WriteLine(a.CompareTo(c));  // "1"

  var listX = new List<string> { a, b, c, };
  var listY = new List<string> { c, a, b, };
  var listZ = new List<string> { b, c, a, };
  listX.Sort();
  listY.Sort();
  listZ.Sort();
  Console.WriteLine(listX.SequenceEqual(listY));  // "False"
  Console.WriteLine(listY.SequenceEqual(listZ));  // "False"
  Console.WriteLine(listX.SequenceEqual(listZ));  // "False"
}

Na parte superior, vemos como a transitividade falha.a é menos do quebeb é menos do quec, aindaa falha em ser menor quec.

Isso vai contra ocomportamento documentado do agrupamento Unicode que afirma que:

... para quaisquer cadeias A, B e C, se A <B e B <C, então A <C.

Agora classificando uma lista coma, b ec é exatamente como tentar classificar as mãos de"Pedra", "Papel" e "Tesoura" no conhecido jogo intransitivo. Uma tarefa impossível.

A última parte do meu exemplo de código acima mostra que o resultado da classificação depende da ordem inicial dos elementos (e não há dois elementos na lista que comparem "igual" (0)).

Linq'slistX.OrderBy(x => x) também é afetado, é claro. Essa classificação deve ser estável, mas você obtém resultados estranhos ao solicitar uma coleção que contenhaa, b ec junto com outras strings.

Eu tentei isso comtudo aCultureInfos na minha máquina (já que esse é um tipo dependente da cultura), incluindo a "cultura invariável", e cada um tem o mesmo problema. Eu tentei isso com o tempo de execução do .NET 4.5.1, mas acredito que as versões mais antigas têm o mesmo bug.

Conclusão: Ao classificar cadeias no .NET com o comparador padrão, os resultados são imprevisíveis se algumas cadeias contiverem hífens.

Quais alterações foram introduzidas no .NET 4.0 que causaram esse comportamento?

Já foi observado que esse comportamento é inconsistente nas diferentes versões da plataforma: no .NET 3.5, as seqüências de caracteres com hífens podem ser classificadas de maneira confiável. Em todas as versões da estrutura, chamarSystem.Globalization.CultureInfo.CurrentCulture.CompareInfo.GetSortKey fornece únicoDeyData para essas strings, então por que elas não são classificadas corretamente?

questionAnswers(1)

yourAnswerToTheQuestion