Question : Pourquoi le comparateur de chaînes par défaut ne parvient-il pas à maintenir la cohérence transitive ?
Je connais ce problème a déjà été mentionnée J'ai donc créé ce nouveau fil de discussion parce que j'ai de nouveau rencontré le problème lors de l'écriture d'un test unitaire.
La comparaison de chaînes par défaut (c'est-à-dire la comparaison sensible à la casse dépendante de la culture que nous obtenons avec string.CompareTo(string)
, Comparer<string>.Default
, StringComparer.CurrentCulture
, string.Compare(string, string)
et d'autres) viole la transitivité lorsque les chaînes contiennent des traits d'union (ou des signes moins, je parle des caractères U+002D).
Voici un exemple simple :
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"
}
Dans la partie supérieure, nous voyons comment la transitivité échoue. a
est inférieur à b
et b
est inférieur à c
et pourtant a
n'est pas inférieur à c
.
Cela va à l'encontre de la comportement documenté de la collation Unicode qui stipule que :
... pour toute chaîne de caractères A, B et C, si A < B et B < C, alors A < C.
Maintenant, le tri d'une liste à l'aide de a
, b
y c
est exactement comme essayer de classer les mains de "Pierre", "Papier" et "Ciseaux" dans le jeu intransitif bien connu. Une tâche impossible.
La dernière partie de mon exemple de code ci-dessus montre que le résultat du tri dépend de l'ordre initial des éléments (et qu'il n'y a pas deux éléments de la liste qui se comparent de manière "égale" ( 0
)).
Linq's listX.OrderBy(x => x)
est également concerné, bien entendu. Ce tri devrait être stable, mais on obtient des résultats étranges lorsqu'on ordonne une collection contenant a
, b
y c
avec d'autres cordes.
J'ai essayé avec todos el CultureInfo
sur ma machine (puisqu'il s'agit d'un tri dépendant de la culture), y compris la "culture invariante", et chacun d'entre eux présente le même problème. J'ai essayé avec le runtime .NET 4.5.1, mais je pense que les versions plus anciennes ont le même problème.
Conclusion : Lorsque l'on trie des chaînes de caractères en .NET avec le comparateur par défaut, les résultats sont imprévisibles si certaines chaînes contiennent des traits d'union.
Quels sont les changements introduits dans .NET 4.0 qui ont provoqué ce comportement ?
Il a déjà été observé que ce comportement n'est pas cohérent entre les différentes versions de la plateforme : dans .NET 3.5, les chaînes de caractères avec des traits d'union peuvent être triées de manière fiable. Dans toutes les versions du framework, l'appel à System.Globalization.CultureInfo.CurrentCulture.CompareInfo.GetSortKey
fournit un service unique de DeyData
pour ces chaînes, alors pourquoi ne sont-elles pas triées correctement ?