Преобразование типов данных с помощью методов приведения и преобразования в C#.
Предположим, что вы являетесь разработчиком программного обеспечения в команде, работающей над автоматизацией формы медицинского приема. Вам поручено разработать функции приложения для сбора данных, вводимых медицинским техником перед приемом пациента врачом. Техник может использовать приложение для записи даты и времени, возраста пациента, роста, веса, пульса и кровяного давления. В приложении также предусмотрены текстовые поля для ввода другой информации, например, причины визита, текущих рецептов и других данных. Вы работаете с большим количеством данных, которые представлены в различных типах. Для прототипа вы создадите консольное приложение и будете собирать все вводимые данные в виде строк.
Поскольку данные изначально вводятся в виде строк, в коде вам придется время от времени менять значения из одного типа данных в другой. Простой пример - любая математическая операция, которую вы хотите выполнить со строковыми данными. Сначала вам нужно будет изменить значение на числовой тип данных, например int, а затем вы сможете выполнить эту операцию. В качестве альтернативы можно отформатировать и вывести числовое значение для сводного отчета с помощью строковой интерполяции.
При необходимости вы используете различные техники для изменения типа данных. Вы узнаете, когда следует использовать одну технику вместо другой и когда та или иная техника может привести к потере данных.
К концу этого модуля вы сможете взять под контроль данные в своих приложениях, зная, когда нужно применить правильную технику для изменения типов данных при необходимости.
Цели обучения
В этом модуле вы узнаете как:
- Использовать оператор приведения для приведения значения к другому типу данных.
- Использовать методы преобразования для преобразования значения в другой тип данных.
- Защищать данные от потери при выполнении операций приведения или преобразования.
- Использовать метод TryParse() для безопасного преобразования строки в числовой тип данных.
Изучите приведение и преобразование типов данных
Существует несколько методов преобразования типов данных. Выбор техники зависит от ответа на два важных вопроса:
- Возможно ли, в зависимости от значения, что попытка изменить тип данных значения приведет к возникновению исключения во время выполнения?
- Возможно ли, в зависимости от значения, что попытка изменить тип данных значения приведет к потере информации?
В этом упражнении вы разберетесь с этими вопросами, последствиями ответов на них и тем, какую технику следует использовать при необходимости изменить тип данных.
Вопрос: Возможно ли, что попытка изменить тип данных значения приведет к возникновению исключения во время выполнения?
Компилятор C# пытается приспособиться к вашему коду, но не компилирует операции, которые могут привести к исключению. Когда вы поймете основную задачу компилятора C#, вам будет легче понять, почему он функционирует определенным образом.
Напишите код, который пытается сложить int и строку и сохранить результат в int
- Введите следующий код в Visual Studio
int first = 2;
string second = "4";
int result = first + second;
Console.WriteLine(result);
- Здесь вы пытаетесь сложить значения 2 и 4. Значение 4 имеет тип string. Будет ли это работать?
- Вы видите ошибку
error CS0029: Cannot implicitly convert type 'string' to 'int'
- Потратьте минуту на то, почему компилятор не смог выполнить первый пример кода.
Важная часть сообщения об ошибке, (3,14): error CS0029: Cannot implicitly convert type 'string' to 'int', говорит о том, что проблема связана с использованием типа данных string.
Но почему компилятор C# не может просто обработать ошибку? В конце концов, можно поступить наоборот - конкатенировать число в строку и сохранить ее в строковой переменной. Здесь вы меняете тип данных переменной result с int на string.
- Обновите ваш код на следующий
int first = 2;
string second = "4";
string result = first + second;
Console.WriteLine(result);
- Сохраните файл кода, а затем запустите его с помощью Visual Studio. Вы должны увидеть следующий результат:
24
Вывод математически неверен, но завершается объединением значений в виде символов "2" и "4".
- Рассмотрите еще раз первый пример кода, в котором переменная result имеет тип int. Код с сообщением об ошибке
int first = 2;
string second = "4";
int result = first + second;
Console.WriteLine(result);
Почему компилятор C# не может понять, что вы хотите рассматривать переменную second, содержащую 4, как число, а не как строку?
Компиляторы выполняют безопасные преобразования
Компилятор C# видит потенциальную проблему. Переменная second
имеет тип string
, поэтому она может быть установлена в другое значение, например «hello». Если компилятор C# попытается преобразовать «hello» в число, это вызовет исключение во время выполнения. Чтобы избежать такой возможности, компилятор C# не выполняет неявное преобразование из string
в int
.
С точки зрения компилятора C#, более безопасной операцией будет преобразование int
в строку и выполнение конкатенации.
Если вы собираетесь выполнить сложение с использованием строки, компилятор C# требует от вас более явного контроля над процессом преобразования данных. Другими словами, он заставляет вас быть более вовлеченным в процесс, чтобы вы могли принять надлежащие меры предосторожности для обработки возможности того, что при преобразовании может возникнуть исключение.
Если вам нужно изменить значение из исходного типа данных в новый тип данных и это изменение может привести к исключению во время выполнения, необходимо выполнить преобразование данных.
Для выполнения преобразования данных можно использовать один из нескольких методов:
- Использовать вспомогательный метод для типа данных
- Использовать вспомогательный метод для переменной
- Использовать методы класса Convert.
Далее в этом разделе вы рассмотрите несколько примеров этих методов преобразования данных.
Вопрос: Возможно ли, что попытка изменить тип данных значения приведет к потере информации?
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
int myInt = 3;
Console.WriteLine($"int: {myInt}");
decimal myDecimal = myInt;
Console.WriteLine($"decimal: {myDecimal}");
- Сохраните файл кода, а затем запустите его с помощью Visual Studio. Вы должны увидеть следующий результат:
int: 3
decimal: 3
Ключом к этому примеру является эта строка кода:
decimal myDecimal = myInt;
Поскольку любое значение int
может легко поместиться внутри десятичной дроби, компилятор выполняет преобразование.
Термин «расширяющее преобразование» означает, что вы пытаетесь преобразовать значение из типа данных, который может содержать меньше информации, в тип данных, который может содержать больше информации. В этом случае значение, хранящееся в переменной типа int
, преобразованное в переменную типа decimal
, не теряет информации.
Когда вы знаете, что выполняете расширяющее преобразование, вы можете положиться на неявное преобразование. Компилятор обрабатывает неявные преобразования.
Выполните приведение
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
decimal myDecimal = 3.14m;
Console.WriteLine($"decimal: {myDecimal}");
int myInt = (int)myDecimal;
Console.WriteLine($"int: {myInt}");
Чтобы выполнить приведение, вы используете оператор приведения () для окружения типа данных, а затем помещаете его рядом с переменной, которую хотите преобразовать (пример: (int)myDecimal). Вы выполняете явное преобразование к определенному типу данных приведения (int).
- Сохраните файл кода, а затем запустите его с помощью Visual Studio. Вы должны увидеть следующий результат:
decimal: 3.14
int: 3
Ключом к этому примеру является эта строка кода:
int myInt = (int)myDecimal;
В переменной myDecimal хранится значение, которое имеет точность после десятичной точки. Добавляя инструкцию приведения (int), вы сообщаете компилятору C#, что понимаете возможность потери этой точности, и в данной ситуации это нормально. Вы сообщаете компилятору, что выполняете намеренное преобразование, явное преобразование.
Определите, является ли ваше преобразование "расширяющим" или "сужающим".
Термин «сужающее преобразование» означает, что вы пытаетесь преобразовать значение из типа данных, который может содержать больше информации, в тип данных, который может содержать меньше информации. В этом случае вы можете потерять такую информацию, как точность (то есть количество значений после десятичной точки). Примером может служить преобразование значения, хранящегося в переменной типа decimal, в переменную типа int. Если вы распечатаете эти два значения, то, возможно, заметите потерю информации.
Когда вы знаете, что выполняете сужающее преобразование, необходимо выполнить приведение. Приведение - это указание компилятору C#, что вы знаете, что точность может быть потеряна, но готовы с этим смириться.
Если вы не уверены, потеряете ли вы данные при преобразовании, напишите код для выполнения преобразования двумя разными способами и понаблюдайте за изменениями. Разработчики часто пишут небольшие тесты, чтобы лучше понять поведение, как показано в следующем примере.
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
decimal myDecimal = 1.23456789m;
float myFloat = (float)myDecimal;
Console.WriteLine($"Decimal: {myDecimal}");
Console.WriteLine($"Float : {myFloat}");
- Сохраните файл с кодом, а затем запустите его с помощью Visual Studio. Вы должны увидеть результат, похожий на этот:
Decimal: 1.23456789
Float : 1.2345679
Из вывода видно, что приведение decimal к float является сужающим преобразованием, поскольку теряется точность.
Выполнение преобразования данных
Ранее говорилось, что изменение значения из одного типа данных в другой может вызвать исключение во время выполнения программы, и вам следует выполнить преобразование данных. Для преобразования данных можно использовать три способа:
- Использовать вспомогательный метод для переменной
- Использовать вспомогательный метод для типа данных
- Использовать методы класса Convert
Используйте функцию ToString() для преобразования числа в строку
У каждой переменной типа данных есть метод ToString(). То, что делает метод ToString(), зависит от того, как он реализован для данного типа. Однако для большинства примитивов он выполняет расширяющее преобразование. Хотя это и не является строго необходимым (поскольку в большинстве случаев вы можете полагаться на неявное преобразование), это может показать другим разработчикам, что вы понимаете, что делаете, и это намеренно.
Вот быстрый пример использования метода ToString() для явного преобразования значений int в string.
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
int first = 5;
int second = 7;
string message = first.ToString() + second.ToString();
Console.WriteLine(message);
- Сохраните файл с кодом, а затем используйте Visual Studio для выполнения кода. После выполнения кода на выходе должно появиться конкатенация двух значений:
57
Преобразуйте string в int с помощью вспомогательного метода Parse()
Большинство числовых типов данных имеют метод Parse(), который преобразует строку в данный тип данных. В данном случае вы используете метод Parse() для преобразования двух строк в значения int, а затем складываете их вместе.
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
string first = "5";
string second = "7";
int sum = int.Parse(first) + int.Parse(second);
Console.WriteLine(sum);
- Сохраните файл с кодом, а затем используйте Visual Studio для выполнения кода. После выполнения кода на выходе должна появиться сумма двух значений:
12
- Потратьте минуту на то, чтобы попытаться обнаружить потенциальную проблему в предыдущем примере кода? Что если переменные first или second имеют значения, которые не могут быть преобразованы в int? Во время выполнения будет выброшено исключение. Компилятор и среда выполнения C# ожидают, что вы заранее спланируете и предотвратите "незаконные" преобразования. Вы можете смягчить исключение во время выполнения несколькими способами.
Самый простой способ смягчить эту ситуацию - использовать TryParse(), который является улучшенной версией метода Parse().
Преобразование string в int с помощью класса Convert
Класс Convert имеет множество вспомогательных методов для преобразования значений из одного типа в другой. В следующем примере кода вы преобразуете пару строк в значения типа int.
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
string value1 = "5";
string value2 = "7";
int result = Convert.ToInt32(value1) * Convert.ToInt32(value2);
Console.WriteLine(result);
- Сохраните файл кода, а затем запустите его с помощью Visual Studio. Вы должны увидеть следующий результат:
35
Почему метод называется ToInt32()? Почему не ToInt()? System.Int32 - это название базового типа данных в библиотеке классов .NET, который язык программирования C# сопоставляет с ключевым словом int. Поскольку класс Convert также является частью библиотеки классов .NET, он вызывается по своему полному имени, а не по имени C#. Благодаря определению типов данных как части библиотеки классов .NET, несколько языков .NET, таких как Visual Basic, F#, IronPython и другие, могут совместно использовать одни и те же типы данных и одни и те же классы в библиотеке классов .NET.
Метод ToInt32() имеет 19 перегруженных версий, что позволяет ему принимать практически все типы данных.
Здесь вы использовали метод Convert.ToInt32() со строкой, но, вероятно, вам следует использовать TryParse(), когда это возможно.
Итак, когда же следует использовать класс Convert? Класс Convert лучше всего подходит для преобразования дробных чисел в целые (int), потому что он округляет в большую сторону, как вы и ожидали.
Сравните приведение и преобразование decimal в int
Следующий пример демонстрирует, что происходит при попытке преобразования decomal в int (сужающее преобразование) по сравнению с использованием метода Convert.ToInt32() для преобразования той же decimal в int.
- Удалите или используйте оператор комментария строки //, чтобы закомментировать код из предыдущего шага упражнения, и добавьте следующий код:
int value = (int)1.5m; // casting truncates
Console.WriteLine(value);
int value2 = Convert.ToInt32(1.5m); // converting rounds up
Console.WriteLine(value2);
- Сохраните файл кода, а затем запустите его с помощью Visual Studio. Вы должны увидеть следующий результат:
1
2
Приведение усеченных значений и преобразование округлений
При приведении int value = (int)1.5m; значение float усекается, чтобы результат был равен 1, то есть значение после запятой полностью игнорируется. Вы можете изменить литерал float на 1.999m, и результат приведения будет таким же.
При преобразовании с помощью Convert.ToInt32() буквальное значение float правильно округляется до 2. Если бы вы изменили буквальное значение на 1.499m, оно было бы округлено вниз до 1.
Выводы
Вы рассмотрели несколько важных концепций преобразования и приведения данных:
- Предотвращение ошибки во время выполнения преобразования данных
- Выполните явное приведение, чтобы сообщить компилятору, что вы понимаете риск потери данных
- Положитесь на компилятор, чтобы выполнить неявное приведение при выполнении расширяющего преобразования
- Используйте оператор приведения () и тип данных для выполнения приведения (например, (int)myDecimal)
- Используйте класс Convert, когда вы хотите выполнить сужающее преобразование, но хотите выполнить округление, а не усечение информации
Изучите метод TryParse().
При работе с данными иногда необходимо преобразовать строковые данные в числовый тип данных. Как вы узнали в предыдущем уроке, так как строковый тип данных может содержать нечисловое значение, возможно, что преобразование из string типа числовых данных приводит к ошибке среды выполнения.
Например, приведенный ниже код
string name = "Bob";
Console.WriteLine(int.Parse(name));
вызывает такое исключение:
System.FormatException: 'Input string was not in a correct format.'
Чтобы избежать исключения формата, используйте метод TryParse() для целевого типа данных.
Используйте TryParse()
Метод TryParse() выполняет несколько действий одновременно:
- Он пытается преобразовать строку в заданный числовой тип данных.
- В случае успеха преобразованное значение сохраняется в выходном параметре , как описано в следующем разделе.
- Он возвращает значение, bool указывающее, было ли действие успешным или нет.
Вы можете использовать возвращаемое логическое значение для выполнения действия над значением (например, выполнения какого-либо вычисления) или отображения сообщения, если операция анализа завершилась неудачей.
В этом упражнении вы будете использовать int тип данных, но аналогичный TryParse() метод доступен для всех числовых типов данных.
Выходные параметры
Методы могут возвращать значение или возвращать "void" - то есть они не возвращают никакого значения. Методы также могут возвращать значения через outпараметры, которые определяются так же, как входной параметр, но включают outключевое слово.
TryParse() преобразует строку в целое число
-
Удалите или используйте оператор комментария //, чтобы закомментировать весь код из предыдущих упражнений.
-
Обновите свой код в редакторе кода Visual Studio следующим образом:
string value = "102";
int result = 0;
if (int.TryParse(value, out result))
{
Console.WriteLine($"Measurement: {result}");
}
else
{
Console.WriteLine("Unable to report the measurement.");
}
- Изучите эту строку кода:
if (int.TryParse(value, out result))
При вызове метода с outпараметром необходимо использовать ключевое слово outперед переменной, которая содержит значение. Параметр outназначается переменной resultв коде . Затем вы можете использовать значение, которое содержит параметр, в остальной части кода с помощью переменной .(int.TryParse(value, out result)outresult
Метод int.TryParse()возвращает , trueесли он успешно преобразовал stringпеременную valueв int; в противном случае он возвращает false. Поэтому заключите оператор в оператор if, а затем выполните логику принятия решения соответствующим образом.
Преобразованное значение сохраняется в intпеременной result. intПеременная resultобъявляется и инициализируется перед этой строкой кода, поэтому она должна быть доступна как внутри блоков кода, принадлежащих операторам ifи else, так и вне их.
Ключевое outслово указывает компилятору, что TryParse()метод не только возвращает значение традиционным способом (как возвращаемое значение), но и передает выходные данные через этот двусторонний параметр.
При запуске кода вы должны увидеть следующий вывод:
Measurement: 102
Используйте разобранный int позже в коде
- Чтобы продемонстрировать, что resultпеременная, объявленная ранее, заполняется параметром outи может использоваться далее в вашем коде, обновите свой код в редакторе кода Visual Studio следующим образом:
string value = "102";
int result = 0;
if (int.TryParse(value, out result))
{
Console.WriteLine($"Measurement: {result}");
}
else
{
Console.WriteLine("Unable to report the measurement.");
}
Console.WriteLine($"Measurement (w/ offset): {50 + result}");
- Вы должны увидеть следующий вывод:
Measurement: 102
Measurement (w/ offset): 152
Изучите последнюю строку кода в предыдущем примере.
Console.WriteLine($"Measurement (w/ offset): {50 + result}");
Поскольку result переменная определена вне оператора if, к ней можно получить доступ позже в вашем коде.
Измените строковую переменную на значение, которое невозможно проанализировать
Наконец, рассмотрим другой сценарий, в котором TryParse()
преднамеренно задано неверное значение, которое невозможно преобразовать в int.
- Измените первую строку кода, повторно инициализируйте переменную value другим значением.
string value = "bad";
- Также измените последнюю строку кода, чтобы гарантировать, что результат больше 0, прежде чем показывать второе сообщение.
if (result > 0)
Console.WriteLine($"Measurement (w/ offset): {50 + result}");
- Весь пример кода теперь должен соответствовать следующему коду
string value = "bad";
int result = 0;
if (int.TryParse(value, out result))
{
Console.WriteLine($"Measurement: {result}");
}
else
{
Console.WriteLine("Unable to report the measurement.");
}
if (result > 0)
Console.WriteLine($"Measurement (w/ offset): {50 + result}");
- Сохраните файл кода, а затем используйте Visual Studio для запуска кода. Вы должны получить следующий результат
Unable to report the measurement.
- Изучите последние две строки кода, добавленные в предыдущем примере.
if (result > 0)
Console.WriteLine($"Measurement (w/ offset): {50 + result}");
Поскольку result определяется вне оператора if, к нему можно получить доступ позже в коде, вне блоков кода. Так, result может быть проверен на значение больше нуля, прежде чем разрешить запись result + смещения в качестве выходных данных. Проверка на значение result больше нуля позволяет избежать печати значения смещения после сообщения Невозможно сообщить об измерении.
Выводы
Метод TryParse() - ценный инструмент. Вот несколько коротких идей, которые следует запомнить.
- Используйте
TryParse()
при преобразовании строки в числовой тип данных. TryParse()
возвращает true в случае успешного преобразования и false - в случае неудачи.- Параметры out служат дополнительным средством возврата значения методом. В данном случае параметр out возвращает преобразованное значение.
- Используйте ключевое слово out при передаче аргумента методу, в котором определен параметр out.
Общее задание. Выполните задачу по объединению значений массива строк как строк и как целых чисел
Задачи с кодом закрепляют пройденный материал и помогают обрести уверенность, прежде чем продолжить работу.
В этом модуле представлены две задачи с кодом. В первой задаче вам нужно разделить данные в зависимости от их типа и соответственно соединить или добавить их.
Примеры кода в этом упражнении разработаны на основе настроек культуры en-US и используют точку (.) в качестве десятичного разделителя. Сборка и выполнение кода с настройками культуры, в которых используются другие десятичные разделители (например, запятая ,), может привести к неожиданным результатам или ошибкам. Чтобы устранить эту проблему, замените десятичные разделители периода в примерах кода на местный десятичный разделитель (например, на ,). В качестве альтернативы, чтобы запустить программу, использующую настройки культуры en-US, добавьте следующий код в верхнюю часть программы: using System.Globalization; и после всех остальных операторов using добавьте CultureInfo.CurrentCulture = new CultureInfo(«en-US»);.
- Выделите и удалите все строки кода в редакторе кода Visual Studio. По желанию используйте оператор комментария строки //, чтобы закомментировать весь код из предыдущего шага.
- Чтобы создать массив строк, введите следующий "стартовый" код:
string[] values = { "12.3", "45", "ABC", "11", "DEF" };
- Создайте циклическую структуру, которая будет использоваться для итерации по каждому строковому значению в массиве values.
- Завершите необходимый код, поместив его в блок кода циклической структуры массива. В логике кода необходимо реализовать следующие бизнес-правила:
- Правило 1: Если значение алфавитное, объедините его в сообщение.
- Правило 2: Если значение числовое, добавьте его к общему числу.
- Правило 3: Результат должен соответствовать следующему выводу:
Message: ABCDEF
Total: 68.3
Общее задание. Выполните задание на вывод математических операций в виде определенных типов чисел
Вот второй шанс использовать то, что вы узнали о приведении и преобразовании, для решения задачи по кодированию.
Следующая задача поможет вам понять последствия приведения значений с учетом влияния сужающих и расширяющих преобразований.
-
Удалите или закомментируйте весь код из предыдущего упражнения.
-
Введите следующий «стартовый» код:
int value1 = 11;
decimal value2 = 6.2m;
float value3 = 4.3f;
// Your code here to set result1
// Hint: You need to round the result to nearest integer (don't just truncate)
Console.WriteLine($"Divide value1 by value2, display the result as an int: {result1}");
// Your code here to set result2
Console.WriteLine($"Divide value2 by value3, display the result as a decimal: {result2}");
// Your code here to set result3
Console.WriteLine($"Divide value3 by value1, display the result as a float: {result3}");
- Замените комментарии к коду в исходном коде на свой код, чтобы решить задачу:
- Решите задачу для result1: Разделить значение1 на значение2, вывести результат в виде int
- Решите задачу для result2: Разделите значение2 на значение3, выведите результат в виде decimal
- Решите задачу для result3: Разделите значение3 на значение1, выведите результат в виде float
Решите задачу так, чтобы ваш результат выглядел следующим образом:
Divide value1 by value2, display the result as an int: 2
Divide value2 by value3, display the result as a decimal: 1.4418604651162790697674418605
Divide value3 by value1, display the result as a float: 0.3909091