Огромная часть информации, которую приходится обрабатывать разработчикам и их программам, является текстом. И для того, чтобы уметь эффективно обрабатывать эти данные необходимо знать особенности и операции типов string
и char C#. В этом видео мы как раз и займемся подробнейшим разбором этих и других сопутствующих им классов и структур.
Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!
Символы char C#
Начнем разбор строк мы с составляющих их частей – символов. По своей сути это отдельная буква, цифра или значок, который используется для хранения информации. Примерами символов являются все заглавные и строчные буквы различных алфавитов (А, Б, в, г, D, E, f, g, …), цифры и некоторые числа (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ..) и служебные символы (+, -, %, #, …) и даже некоторые смайлики (😀).
Для того, чтобы посмотреть на список доступных символов можно воспользоваться текстовым редактором Word, перейти на вкладку «Вставить» и нажать на кнопку «Символ». Для вас откроется специальное меню с огромным количеством всевозможных значков. Но даже это не полный список. Здесь отсутствуют некоторые служебные и непечатные символы.

В .NET Framework все символы хранятся в соответствии стандарту Unicode (кодировка для по умолчанию UTF-8 BOM) – то есть, каждый символ занимает до 16 бит в памяти (обычно 8). Именно благодаря этому, у вас есть возможность именовать переменные в Visual Studio используя кириллицу (пожалуйста, никогда так не делайте!).
Весь исходный код статьи доступен на GitHub:
CharAndString/CharOpportunities
Минимальное и максимальное значение char
В C# для хранения символов используется значимый тип System.Char
(не забываем, что это означает, что тип является структурой, а не классом). Сам по себе он достаточно простой и содержит в тебе два неизменяемых поля:
Console.WriteLine(char.MinValue); // \0 Console.WriteLine(char.MaxValue); // \uffff
При этом ни первый, ни второй символ толком прочитать на консоли нам не удастся, они оба являются служебными. Важно понимать, что даже несмотря на то, что \uffff
состоит из нескольких символов на письме, это всего лишь один символ. По сути, это просто шестнадцатеричная запись числа. Мы самостоятельно можем в этом убедиться, если выполним приведение символа к числу:
Console.WriteLine((int)char.MinValue); // 0 Console.WriteLine((int)char.MaxValue); // 65535

Категории символов
Для того, чтобы знать, что именно из себя представляет символ можно использовать статический метод GetUnicodeCategory()
, который возвращает элемент перечисления UnicodeCategory
. Например:
char symbol = 'A'; Console.WriteLine(char.GetUnicodeCategory(symbol)); // UppercaseLetter
С полным списком возможных значений можно ознакомиться в подсказках IntelliSense или в Microsoft Docs.

При необходимости мы можем использовать целый набор соответствующих методов, которые позволяют проверить соответствие символа определенной категории.
char symbol = 'A'; if (char.IsLetter(symbol)) { Console.WriteLine("This is a letter"); } else { Console.WriteLine("It is definitely not a letter"); }

Преобразование регистра символа с учетом и без учета региональных стандартов
Мы без проблем можем преобразовывать друг в друга соответствующие строчные и заглавные буквы. Для этого можно использовать две пары методов: ToLowerInvariant()
и ToUpperInvariant()
, ToLower()
и ToUpper()
. Разница между ними в том, что в первом случае преобразование символа будет выполняться без учета региональных стандартов. Во втором случае по умолчанию будет использоваться набор региональных стандартов, получаемых из свойств вызывающего потока. Либо мы можем самостоятельно передать необходимый параметр CultureInfo
. В некоторых случаях это может повлиять на результат преобразования.
private static void CharToUpperCultureInfo() { var symbol = 'i'; Console.WriteLine(char.ToUpperInvariant(symbol)); // I var cultureInfo = CultureInfo.GetCultureInfo("tr"); // Turkish Console.WriteLine(char.ToUpper(symbol, cultureInfo)); // İ }
Результат выполнения операций приведения в данном случае будет отличаться. В первом случае мы получим латинскую прописную букву I (\u0049
), а во втором – латинская прописная буква İ с надстрочной точкой (\u0130
).

Экземплярные методы char
Нам также предоставляется возможность использовать несколько стандартных экземплярных методов при работе с символами. Например, мы можем выполнять сравнение с помощью метода Equals()
, который проверяет соответствие именно кодов символов, а не их внешнее сходство.
Например, несмотря на то что данные символы выглядят одинаково, они имеют разные коды, а следовательно, не будут равны.
private static void EqualsSymbol() { var s1 = 'μ'; var s2 = 'µ'; Console.WriteLine('μ'.Equals(s1)); // true Console.WriteLine(s1.Equals(s2)); // false Console.WriteLine((int)s1); // 956 Console.WriteLine((int)s2); // 181 }
По этому поводу мне разу же вспомнился прикол, связанный с волшебным языком программирования JavaScript. Там есть отличная возможность немного поднасрать другу на работе, заменив обычный символ вычитания ‘-‘ (\u002D
) на очень похожий на него специальный символ ‘ ’ (\u1680
). А так как JS – это страна чудес, и интерпретатор благополучно пропускает любую дичь, то мы можем получить вот такую магию… И ни одного предупреждения об ошибке!

Если не верите, то можете самостоятельно прямо сейчас открыть консоль Google Chrome и ввести эти команды. А потом у меня спрашивают, за что я не люблю JS…
1 + (-2)
1 + ( 2)
Сравнение char C#
Также мы можем выполнять сравнение символов, так как char
реализует интерфейсы IComparable
и IComparable<Char>
. При этом сравнение выполняется без учета региональных стандартов, поэтому можно не опасаться, это может повлиять на результаты. Сравниваются именно числовые коды символов.
private static void CompareSymbols() { Console.WriteLine((int)'I'); Console.WriteLine((int)'İ'); Console.WriteLine('I'.CompareTo('İ')); // -231 }
Несмотря на то, что это одинаковые символы с культурной точки зрения, сравнение выполняется правильно и мы видим отрицательное значение смещения.
Взаимное преобразование строки и символа
У нас есть возможность выполнять преобразование строки из одного символа в символ и наоборот, символа в строку из одного символа. В первом случае используются методы Parse()
и TryParse()
, во втором – ToString()
.
var str = "CODE BLOG"; var oneSymbolString = "A"; // var error = char.Parse(str); // Строка обязана состоять из одного символа var result = char.Parse(oneSymbolString); Console.WriteLine(result);
Для того, чтобы избегать выбрасывания исключения при попытке приведения строки к символу лучше всего использовать более безопасную версию метода TryParse()
.
if (char.TryParse(str, out char symbol)) { Console.WriteLine(symbol); } else { Console.WriteLine($"Преобразовать [{str}] в символ не удалось"); }
Важной особенностью является то, что возвращаемое значение у обоих этих методов использует кодировку UTF-16, из-за чего может возникать избыточное использование памяти. Благо компилятор обычно без проблем справляется с этой особенностью, поэтому ничего специально менять не нужно.
При преобразовании символа в cтроку все достаточно просто и безопасно, никаких дополнительных проверок выполнять не нужно.
var c = 'G'; var s = c.ToString(); Console.WriteLine(s);
Преобразование символа в числовое значение
Так как цифры и некоторые числа тоже могут быть символами у нас должна быть возможность получать цифровые значения. Для этого мы можем воспользоваться методом GetNumericValue()
.
private static void SymbolToNumber() { var three = char.GetNumericValue('\u0033'); // цифра 3 Console.WriteLine(three); // 3 Console.WriteLine((int)'\u0033'); // 51 var quarter = char.GetNumericValue('\u00bc');// дробь одна четвертая ¼ Console.WriteLine(quarter); // 0.25 Console.WriteLine((int)'\u00bc'); // 188 var letter = char.GetNumericValue('a'); // не число Console.WriteLine(letter); // -1 Console.WriteLine((int)'a'); // 97 }
Обратите внимание, что данный метод возвращает тип double
, а если в качестве аргумента передается не числовой символ, то возвращается значение -1.
Результат выполнения этого метода кардинально отличается от простого приведения символа к int
, так как при приведении, по сути, просто возвращается десятичный код символа.
Преобразование числа в символ и наоборот
Существует несколько способов преобразования числа в символ и в обратном направлении. При этом некоторые будут работать быстрее, чем другие. Давайте разберем их в порядке ухудшения.
Приведение типа – самый простой и эффективный способ, так как компилятору не нужно использовать дополнительные IL-команды для вызова методов. Мы с легкостью можем привести int к соответствующему этому коду символу. Приведение может быть как проверяемым, так и непроверяемым (checked
или unchecked
по умолчанию).
Тип Convert – мы можем использовать специализированный для приведения класс Convert
, который содержит в себе несколько статических перегруженных методов для работы с char
. За счет этого упаковка выполняться не будет, но дополнительные IL-команды для вызова методов будут сгенерированы компилятором. В данном случае операции могут быть только проверяемые.
Интерфейс IConvertible – как и многие другие структуры FCL, char
явно реализует интерфейс IConvertible
, что позволяет выполнять приведение типов. Но так как char
– это структура, то при работе через интерфейсную переменную будет выполняться упаковка, что негативно скажется на потреблении ресурсов системы и производительности приложения. Операции также будут проверяемыми.
Важно помнить, что так как методы интерфейса IConvertible
реализованы явно, то напрямую в переменной они недоступны. Необходимо сначала выполнить приведение к интерфейсной переменной и только после этого соответствующие методы ToInt32()
и ToChar()
станут доступны для использования.
В качестве аргумента данные методы принимают IFormatProvider
, с помощью которого можно передавать региональные стандарты. Но зачастую в этом нет необходимости, поэтому можно просто передавать null
.
В случае же если выполнить преобразование невозможно, например, привести переменную char
к bool
, то будет генерироваться исключение InvalidCastException
.
Рассмотрим все эти способы приведения и их особенности на примере:
Snippet
private static void ConvertChar() { // Приведение типа var a = (char)65; // Буква А Console.WriteLine($"a = {a}"); var number = (int)'A'; // Число 65 Console.WriteLine($"number = {number}"); var number2 = (int)a; // Число 65 Console.WriteLine($"number2 = {number2}"); var negativeNumber = -3; var tooBigNumber = 65601; var d = 2.573; var negativeDefault = (char)negativeNumber; // \uFFFD � Console.WriteLine($"negativeDefault = {negativeDefault}"); var tooBigDefault = (char)tooBigNumber; // Буква А Console.WriteLine($"tooBigDefault = {tooBigDefault}"); var tooBigUnchecked = unchecked((char)tooBigNumber); // Буква А Console.WriteLine($"tooBigUnchecked = {tooBigUnchecked}"); //var tooBigChecked = checked((char)tooBigNumber); // Ошибка // Console.WriteLine(tooBigChecked); //var negativeChecked = checked((char)negativeNumber); // Ошибка //Console.WriteLine(negativeChecked); var doubleConvert = (char)d; // \u0002 ☻ - // дробная часть числа игнорируется Console.WriteLine($"doubleConvert = {doubleConvert}"); // Тип Convert var convertNumber = Convert.ToChar(65); // Буква А Console.WriteLine($"convertNumber = {convertNumber}"); var convertChar = Convert.ToInt32('A'); // Число 65 Console.WriteLine($"convertChar = {convertChar}"); //var tooBigConvert = Convert.ToChar(tooBigNumber); // Ошибка //Console.WriteLine(tooBigConvert); // Интерфейс IConvertible var iConvertibleNumber = ((IConvertible)65).ToChar(null); // Буква А Console.WriteLine($"iConvertibleNumber = {iConvertibleNumber}"); var iConvertivleChar = ((IConvertible)'A').ToInt32(null); // Число 65 Console.WriteLine($"iConvertivleChar = {iConvertivleChar}"); }
Char C#
Мы рассмотрели все основные особенности по работе с символьным типом char
в языке программирования C#. Сталкиваться с ним вам придется достаточно часто, поэтому не поленитесь самостоятельно разобрать все примеры исходного кода на практике. А в следующей статье мы пойдем дальше и будем говорить о строках string
.
Советую прочитать предыдущую статью — Интерфейсы C# на практике.
А также подписывайтесь на группу ВКонтакте, Telegram, Инстаграм и YouTube-канал. Там еще больше полезного и интересного для программистов.