字符串的哈希代码在.NET Core 2.1中被破坏,但在2.0中有效

Mår*_*röm 14 c# string hashcode .net-core

我最近将我的一个项目从.NET Core 2.0升级到.NET Core 2.1.这样做后,我的几个测试开始失败.

在缩小此范围之后,我发现在.NET Core 2.1中,使用带有文化感知的比较器和字符串排序比较选项来计算字符串的哈希码是不可能的.

我创建了一个可以重现我的问题的测试:

[TestMethod]
public void Can_compute_hash_code_using_invariant_string_sort_comparer()
{
    var compareInfo = CultureInfo.InvariantCulture.CompareInfo;
    var stringComparer = compareInfo.GetStringComparer(CompareOptions.StringSort);
    stringComparer.GetHashCode("test"); // should not throw!
}
Run Code Online (Sandbox Code Playgroud)

我在几个框架上测试了它,结果如下:

  • .NET Core 2.0:✔通过
  • .NET Core 2.1:✖失败
  • .NET Framework 4.7:✖失败
  • .NET Framework 4.6.2:✖失败

失败的时候ArgumentExceptionCompareInfo.GetHashCodeOfString说:

标志的值无效

现在,我的问题:

  1. 为什么CompareOptions.StringSort在计算哈希码时不允许使用它?

  2. 为什么在.NET Core 2.0中允许它?`

据我所知,CompareOptions.StringSort只影响字符串的相对排序顺序,不应影响哈希码计算.MSDN说:

StringSort指示字符串比较必须使用字符串排序算法.在字符串排序中,连字符和撇号以及其他非字母数字符号位于字母数字字符之前.

Mår*_*röm 2

corefx 团队已确认这是 .NET Core 2.1 以及 4.6+ 版本的完整 .NET Framework 中的错误。

他们还承认,很难在完整框架中更改此行为,因此可能会考虑在 .NET Core 2.1+ 中保持该行为不变,以保持 .NET Core 和完整框架之间的一致性。

一种可能的解决方法是使用这样的类:

internal sealed class CultureAwareStringSortComparer : StringComparer
{
    public CultureAwareStringSortComparer(
        CompareInfo compareInfo, 
        CompareOptions options = CompareOptions.StringSort)
    {
        Requires.ArgNotNull(compareInfo, nameof(compareInfo));
        this.SortComparer = compareInfo.GetStringComparer(options);
        this.HashCodeComparer = compareInfo.GetStringComparer(
            options & ~CompareOptions.StringSort);
    }

    internal StringComparer SortComparer { get; }

    internal StringComparer HashCodeComparer { get; }

    public override int Compare(string x, string y) => this.SortComparer.Compare(x, y);

    public override bool Equals(string x, string y) => this.SortComparer.Equals(x, y);

    public override int GetHashCode(string obj) => this.HashCodeComparer.GetHashCode(obj);
}
Run Code Online (Sandbox Code Playgroud)