compare函数 - string.compare c#



为什么默认字符串比较器无法保持传递一致性? (1)

我知道之前已经注意到这个问题,或多或少简洁,但我仍然创建这个新线程,因为我在编写单元测试时再次遇到了这个问题。

默认字符串比较(即我们用string.CompareTo(string)Comparer<string>.DefaultStringComparer.CurrentCulturestring.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小于bb小于c ,但a小于c

这与Unicode归类的记录行为相反,后者声明:

...对于任何字符串A,B和C,如果A <B且B <C,则A <C。

现在用abc对列表进行排序就像在着名的不及物游戏中对“Rock”,“Paper”和“Scissors”的牌进行排名一样。 不可能的任务。

我上面的代码示例的最后一部分显示排序的结果取决于元素的初始顺序(并且列表中没有两个元素比较“等于”( 0 ))。

当然,Linq的listX.OrderBy(x => x)也会受到影响。 这应该是一个稳定的排序,但是在订购包含abc以及其他字符串的集合时会得到奇怪的结果。

我尝试使用我机器上的所有 CultureInfo (因为这是一种依赖于文化的排序),包括“不变文化”,每个人都有同样的问题。 我尝试使用.NET 4.5.1运行时,但我相信旧版本具有相同的错误。

结论:使用默认比较器在.NET中对字符串进行排序时,如果某些字符串包含连字符,则结果是不可预测的。

.NET 4.0中引入了哪些更改导致了这种行为?

已经观察到这种行为在不同版本的平台上不一致:在.NET 3.5中,带有连字符的字符串可以被可靠地排序。 在框架的所有版本中,调用System.Globalization.CultureInfo.CurrentCulture.CompareInfo.GetSortKey为这些字符串提供唯一的DeyData ,那么为什么它们不能正确排序?


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);
}




string-comparison