Хеш-код строки не работает в .NET Core 2.1, но работает в 2.0
Недавно я обновил один из своих проектов с .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: ✔ ПРОЙДИТЕ
- .NET Core 2.1: ✖ FAIL
- .NET Framework 4.7: ✖ FAIL
- .NET Framework 4.6.2: ✖ FAIL
При CompareInfo.GetHashCodeOfString
ArgumentException
CompareInfo.GetHashCodeOfString
из CompareInfo.GetHashCodeOfString
говоря:
Значение флагов неверно
Теперь на мои вопросы:
-
Почему нельзя использовать CompareOptions.StringSort
при вычислении хеш-кода?
-
Почему это было разрешено в .NET Core 2.0?
Насколько я понимаю, CompareOptions.StringSort
влияет только на относительный порядок сортировки строк и не должен влиять на вычисление хеш-кода. MSDN говорит:
StringSort Указывает, что для сравнения строк необходимо использовать алгоритм сортировки строк. При сортировке строк дефис и апостроф, а также другие не буквенно-цифровые символы располагаются перед буквенно-цифровыми символами.
Ответы
Ответ 1
Команда 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);
}