Хеш-код строки не работает в .NET Core 2.1, но работает в 2.0 - PullRequest
0 голосов
/ 30 октября 2018

Я недавно обновил один из своих проектов с .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!
}

Я протестировал его на нескольких платформах со следующими результатами:

  • .NET Core 2.0: ✔ PASS
  • .NET Core 2.1: ✖ FAIL
  • .NET Framework 4.7: ✖ FAIL
  • .NET Framework 4.6.2: ✖ FAIL

При неудаче ArgumentException выбрасывается из CompareInfo.GetHashCodeOfString, говоря:

Недопустимое значение флагов

Теперь на мои вопросы:

  1. Почему нельзя использовать CompareOptions.StringSort при вычислении хеш-кода?

  2. Почему это было разрешено в .NET Core 2.0? `

Насколько я понимаю, CompareOptions.StringSort влияет только на относительный порядок сортировки строк и не должен влиять на вычисление хеш-кода. MSDN говорит:

StringSort Указывает, что для сравнения строк необходимо использовать алгоритм сортировки строк. При сортировке строк дефис и апостроф, а также другие не буквенно-цифровые символы располагаются перед буквенно-цифровыми символами.

1 Ответ

0 голосов
/ 31 октября 2018

Команда corefx подтвердила , что это ошибка в .NET Core 2.1, а также в полной .NET Framework начиная с версии 4.6 +.

Они также признают, что будет трудно изменить это поведение в полной среде, и поэтому могут рассмотреть вопрос о том, чтобы сохранить поведение как есть в .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);
}
...