我知道这个问题has been noted before,或多或少简洁,但我仍然创建这个新线程,因为我在编写单元测试时再次遇到了这个问题。
默认字符串比较(即我们通过string.CompareTo(string)
,Comparer<string>.Default
,StringComparer.CurrentCulture
,string.Compare(string, string)
和其他人获得的与文化相关的区分大小写的比较,违反了传递性当字符串包含连字符(或减号,我说的是普通的U + 002D字符)。
这是一个简单的复制品:
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"
}
在上半部分,我们看到传递性如何失败。 a
小于b
,b
小于c
,但a
未能小于c
。
这与Unicode排序规则的documented behavior相反,后者声明:
...对于任何字符串A,B和C,如果A&lt; B和B&lt; C,然后A&lt;下进行。
现在使用a
,b
和c
对列表进行排序,就像在众所周知的不及物游戏中对"Rock", "Paper" and "Scissors"的牌次进行排名一样。这是一项不可能完成的任务。
上面代码示例的最后一部分显示排序的结果取决于元素的初始顺序(并且列表中没有两个元素比较“等于”(0
))。 / p>
当然,Linq的listX.OrderBy(x => x)
也会受到影响。这应该是一个稳定的排序,但是在订购包含a
,b
和c
以及其他字符串的集合时会得到奇怪的结果。
我尝试使用所有我机器上的CultureInfo
(因为这是一种依赖于文化的排序),包括“不变文化”,并且每个人都拥有同样的问题。我尝试使用.NET 4.5.1运行时,但我相信旧版本有相同的错误。
结论:使用默认比较器在.NET中对字符串进行排序时,如果某些字符串包含连字符,则结果是不可预测的。
导致此行为的.NET 4.0中引入了哪些更改?
已经观察到这种行为在不同版本的平台上不一致:在.NET 3.5中,带有连字符的字符串可以被可靠地排序。在框架的所有版本中,调用System.Globalization.CultureInfo.CurrentCulture.CompareInfo.GetSortKey
为这些字符串提供唯一的DeyData
,那么为什么它们不能正确排序?
答案 0 :(得分:3)
Microsoft Connect Discussion 以下是一些解决方法:
static int CompareStringUsingSortKey(string s1, string s2)
{
SortKey sk1 = CultureInfo.InvariantCulture.CompareInfo.GetSortKey(s1);
SortKey sk2 = CultureInfo.InvariantCulture.CompareInfo.GetSortKey(s2);
return SortKey.Compare(sk1, sk2);
}