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

C# 为什么默认字符串比较器无法保持可传递的一致性?,c#,.net,string,sorting,string-comparison,C#,.net,String,Sorting,String Comparison,我或多或少清楚地知道这个问题,但我仍然创建了这个新线程,因为我在编写单元测试时再次遇到了这个问题 默认字符串比较(即使用string.CompareTo(string),Comparer.default,StringComparer.CurrentCulture,string.Compare(string,string)和其他方法获得的区域性相关大小写敏感比较)在字符串包含连字符时会违反传递性(或者减号,我指的是普通的U+002D字符) 下面是一个简单的复制: static void Main(

我或多或少清楚地知道这个问题,但我仍然创建了这个新线程,因为我在编写单元测试时再次遇到了这个问题

默认字符串比较(即使用
string.CompareTo(string)
Comparer.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"
}
static void Main()
{
常量字符串a=“fk-”;
常量字符串b=“-fk”;
常量字符串c=“Fk”;
Console.WriteLine(a.CompareTo(b));/“-1”
Console.WriteLine(b.CompareTo(c));/“-1”
Console.WriteLine(a.CompareTo(c));/“1”
var listX=新列表{a,b,c,};
var listY=新列表{c,a,b,};
var listZ=新列表{b,c,a,};
Sort();
listY.Sort();
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排序规则的规定,即:

…对于任何字符串A、B和C,如果A 现在,用
a
b
c
对列表进行排序,就像在著名的不及物性游戏中试图对用户进行排名一样。这是一项不可能完成的任务

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

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

我用我机器上的所有
CultureInfo
s(因为这是一种依赖于区域性的排序)尝试了这个方法,包括“不变区域性”,每个都有相同的问题。我用.NET 4.5.1运行时尝试了这个方法,但我相信旧版本也有相同的错误

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

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

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

以下是一些解决方案代码:

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

这个问题似乎离题了,因为它与其说是一个可回答的问题,不如说是一个咆哮的问题。+1,我以前在中也遇到过同样的问题,所以,我真的不知道为什么,但要解决这个问题,您可以将排序规则更改为
StringComparison.Ordinal
问题:什么时候.NET会修复其损坏的字符串默认比较器?
-->可以无法回答。
如何保持此行为比创建一致的行为更好?
-->基于观点。这不是一个适合SO的问题。@JeppeStigNielsen您描述了一个bug。因此无法回答关于何时修复的问题。这是Microsoft的问题。我投票重新打开此问题,我不是我不确定这是否是一个错误,但这是一个有趣的问题,有一个最小的、完整的、可验证的例子。SO上有人可以肯定地回答这个问题。即使它是一个错误,也有人在SO上澄清了它,然后向Microsoft报告。2014年5月1日,Microsoft引用:“我们正在跟踪,以便在不久的将来在Windows 8上修复此问题。此外,此问题也应在未来的Windows版本中修复。”@BlueRaja DannyPflughoeft,实际上是2013年5月1日。@BlueRaja DannyPflughoeft我已检查(测试)此外,答案链接的Connect条目中描述的bug实际上已经用最新版本的Windows(8.1)解决了然而,
a
b
c
的问题仍然存在。但是,使用
SortKey
的解决方法也可以解决这个问题。因此,微软的解决方案可能只是部分。这种比较方法将非常缓慢。使用
de>ConditionalWeakTable
来存储与每个字符串对象关联的排序键;如果多个字符串对象包含相同的字符,可能还值得使用某种弱散列映射;但是,我不确定在弱散列映射中查找字符串的成本与为其创建新排序键的成本相比如何。我st做了一个快速测试;似乎每次比较生成CompareInfo的速度比使用OrdinalIgnoreCase慢50倍多一点。使用
ConditionalWeakTable
进行查找会使速度降低约5倍。如果存储CompareInfo对象而不是字符串,则性能与使用OrdinalIgnoreCase相当。