Почему методы С# System.Char для тестов свойств Unicode имеют две перегрузки?

В методах System.Char мы видим два метода проверки, является ли символ символом:

public static bool IsSymbol(string s, int index)
public static bool IsSymbol(char c)

а также для других тестов свойств: IsLower, IsLetter и т.д.

Почему это дублирование? Есть ли какая-то причина предпочесть Char.IsSymbol(s, idx) над Char.IsSymbol(s[idx])?

Ответ 1

На поверхности обе перегрузки кажутся функционально одинаковыми, однако сверление до вызова InternalGetUnicodeCategory покажет, что они приводят к вызовам разных перегрузок CharUnicodeInfo.GetUnicodeCateogry.

Перегрузка string,int заканчивается через преобразование UTF32 через InternalConvertToUtf32 до вызова той же единственной функции char InternalGetUnicodeCategory. Это объясняет возможность декодирования суррогатных пар в кодированном символе UTF16.

   internal static UnicodeCategory InternalGetUnicodeCategory(String value, int index) {
        Contract.Assert(value != null, "value can not be null");
        Contract.Assert(index < value.Length, "index < value.Length");

        return (InternalGetUnicodeCategory(InternalConvertToUtf32(value, index)));
    }

Проверьте реализацию конверсии, если хотите.

Почему вы это спрашиваете? Ну, ответ на этот вопрос заключается в том, что .Net поддерживает текстовые элементы. Microsoft заявляет:

Документация MSDN по поддержке Unicode для суррогатных пар

Текстовый элемент - это единица текста, которая отображается как один символ, называемый графем. Текстовым элементом может быть базовый символ, суррогатная пара или комбинационная последовательность символов.

Хотя я не верю, что функция IsSymbol и ее родственники могут декодировать Graphemes или комбинировать последовательности символов, причиной выноска Text Elements является то, что они могут быть определены как суррогатная пара, и как таковой необходимо будет декодировать через перегрузку string,int IsSymbol(), IsLetter() и т.д....

Это означает, что передача суррогатной пары через перегрузку char приведет к неправильному результату, потому что символ в строке может быть суррогатной парой. Вы не можете предположить, что 16-битная кодировка представляет один символ, и передача символа строки в указанном индексе сделает это предположение.

Поскольку суррогатные пары могут быть представлены в строке в .Net, было бы разумно, что если вы имеете дело со строкой, которая может содержать одну из них, перегрузка IsSymbol(string s, int index) была бы более подходящей, чтобы охватить случай, когда одна из этих пар присутствовала.

Конкретный пример, где результаты отличаются друг от друга:

string s = char.ConvertFromUtf32(128204); // "📌"

Debug.Assert(char.IsSymbol(s[0]) == char.IsSymbol(s, 0)); // Fails