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

Jep*_*sen 30 .net c# sorting string string-comparison

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

默认的字符串比较(即文化相关的区分大小写的比较,我们得到有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"
}
Run Code Online (Sandbox Code Playgroud)

在上半部分,我们看到传递性如何失败.a小于b,b小于c,但a不小于c.

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

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

现在排序一个列表a,b以及c酷似试图排名的手"摇滚","纸张"和"剪刀"的知名游戏不及.一项不可能的任务

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

listX.OrderBy(x => x)当然,Linq 也受到了影响.这应该是一个稳定的排序,但你订购收集含有时得到奇怪的结果a,bc与其他字符串连接在一起.

我想这一切CultureInfo我的机器上S(因为这是一个文化有关的排序),包括"不变文化",和每一个有同样的问题.我尝试使用.NET 4.5.1运行时,但我相信旧版本具有相同的错误.

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

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

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

Kev*_*ook 3

Microsoft Connect 讨论 以下是一些解决方法的代码:

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);
}
Run Code Online (Sandbox Code Playgroud)