# Convert data types using casting and conversion techniques Take control of the data in your applications, knowing when to apply the correct technique to change data types as needed. ### Learning objectives Use the casting operator to cast a value into a different data type. Use conversion methods to convert a value into a different data type. Guard against the loss of data when performing a cast or conversion operation. Use the `TryParse()` method to safely convert a string into a numeric data type. ## Introduction Suppose you're a software developer on a team working on medical intake form automation. You're tasked with delivery of the application features for collecting data entered by a medical technician before the doctor sees the patient. The technician can use the application to record the date and time, patient age, height, weight, pulse, and blood pressure. The application also provides text fields for other information, such as the reason for the visit, current prescriptions, and other items. You work with many data that is in a mix of data types. For the prototype, you'll build a console application and collect all of the input as `strings`. Because the input is initially input as a string, you need to occasionally change values from one data type into another in the code. A simple example is any mathematical operation you want to perform with string data. You would first need to change the value into a numeric data type, like `int`, and then you could manipulate the operation. Alternatively, you may want to format and output a numeric value for a summary report using string interpolation. You use different techniques to change a data type when necessary. You learn when to use one technique over another, and when a given technique might risk the loss of data. By the end of this module, you're able to take control of the data in your applications, knowing when to apply the correct technique to change data types as needed. ### Learning objectives In this module, you'll: - Use the casting operator to cast a value into a different data type. - Use conversion methods to convert a value into a different data type. - Guard against the loss of data when performing a cast or conversion operation. - Use the `TryParse()` method to safely convert a string into a numeric data type. --- ## Exercise ### Explore data type casting and conversion There are multiple techniques to perform a data type conversion. The technique you choose depends on your answer to two important questions: - Is it possible, depending on the value, that attempting to change the value's data type would throw an exception at run time? - Is it possible, depending on the value, that attempting to change the value's data type would result in a loss of information? In this exercise, you work your way through these questions, the implications of their answers, and which technique you should use when you need to change the data type. #### Prepare your coding environment Create a new console application. At the Terminal command prompt type: `dotnet new console -o ./path_to/Project` or `dotnet new console -n "Project"` and then press Enter. This .NET CLI command uses a .NET program template to create a new C# console application project in the specified folder location. You use this C# console project to create, build, and run code samples during this module. Close the Terminal panel. ### Question **Is it possible that attempting to change the value's data type would throw an exception at run time?** The C# compiler attempts to accommodate your code, but doesn't compile operations that could result in an exception. When you understand the C# compiler's primary concern, understanding why it functions a certain way is easier. #### Write code that attempts to add an `int` and a `string` and save the result in an `int` Type the following code into the Visual Studio Code Editor: ```cs int first = 2; string second = "4"; int result = first + second; Console.WriteLine(result); ``` Here, you're attempting to add the values `2` and `4`. The value `4` is of type `string`. Will this work? At the Terminal command prompt, to run your code, type `dotnet run` and then press Enter. You should see the following approximate output Windows: ```txt C:\Users\someuser\Documents\csharpprojects\TestProject\Program.cs(3,14): error CS0029: Cannot implicitly convert type 'string' to 'int' ``` Linux: ```txt /home/user/Projects/TestProject/Program.cs(3,14): error CS0029: Cannot implicitly convert type 'string' to 'int' ``` > Note > If you see a message saying "Couldn't find a project to run", ensure that the Terminal command prompt displays the expected TestProject folder location. For example: `C:\Users\someuser\Documents\csharpprojects\TestProject>` Take a minute to consider why the compiler was unable to run the first code sample. The important part of the error message, `(3,14): error CS0029: Cannot implicitly convert type 'string' to 'int'`, tells you the problem is with the use of the string data type. But why can't the C# Compiler just handle the error? After all, you can do the opposite to concatenate a number to a `string` and save it in a string variable. Here, you change the data type of the `result` variable from `int` to string. Update your code in the editor as follows: ```cs int first = 2; string second = "4"; string result = first + second; Console.WriteLine(result); ``` Save your code file, and then run your code. You should observe the following output: ```txt 24 ``` The output is mathematically incorrect but completes by combining the values as the characters "2" and "4". Examine, once again, the first code example where the `result` variable is of type `int`. The code with the error message. ```cs int first = 2; string second = "4"; int result = first + second; Console.WriteLine(result); ``` Why can't the C# compiler figure out that you want to treat the variable `second` containing `4` as a number, not a `string`? ### Compilers make safe conversions The C# compiler sees a potential problem in the making. The variable `second` is of type `string`, so it might be set to a different value like `"hello"`. If the C# compiler attempted to convert "hello" to a number that would cause an exception at runtime. To avoid this possibility, the C# compiler doesn't implicitly perform the conversion from `string` to `int` for you. From the C# compiler's perspective, the safer operation would be to convert `int` into a `string` and perform concatenation instead. If you intend to perform addition using a string, the C# compiler requires you to take more explicit control of the process of data conversion. In other words, it forces you to be more involved so that you can put the proper precautions in place to handle the possibility that the conversion could throw an exception. If you need to change a value from the original data type to a new data type and the change could produce an exception at run time, you must perform a data conversion. To perform data conversion, you can use one of several techniques: - Use a helper method on the data type - Use a helper method on the variable - Use the Convert class' methods You look at a few examples of these techniques for data conversion later in this unit. ### Question **Is it possible that attempting to change the value's data type would result in a loss of information?** Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs int myInt = 3; Console.WriteLine($"int: {myInt}"); decimal myDecimal = myInt; Console.WriteLine($"decimal: {myDecimal}"); ``` Save your code file, and then run your code. You should see the following output: ```txt int: 3 decimal: 3 ``` The key to this example is this line of code: ```cs decimal myDecimal = myInt; ``` Since any `int` value can easily fit inside of a `decimal`, the compiler performs the conversion. The term *widening* conversion means that you're attempting to convert a value **from** a data type that could hold less information **to** a data type that can hold *more* information. In this case, a value stored in a variable of type `int` converted to a variable of type `decimal`, doesn't lose information. When you know you're performing a widening conversion, you can rely on implicit conversion. The compiler handles implicit conversions. ### Perform a cast Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs decimal myDecimal = 3.14m; Console.WriteLine($"decimal: {myDecimal}"); int myInt = (int)myDecimal; Console.WriteLine($"int: {myInt}"); ``` To perform a cast, you use the casting operator `()` to surround a data type, then place it next to the variable you want to convert (example: `(int)myDecimal`). You perform an explicit conversion to the defined cast data type (`int`). Save your file, and then run your code. You should see the following output: ```txt decimal: 3.14 int: 3 ``` The key to this example is this line of code: ```cs int myInt = (int)myDecimal; ``` The variable `myDecimal` holds a value that has precision after the decimal poin t. By adding the casting instruction `(int)`, you're telling the C# compiler that you understand it's possible you'll lose that precision, and in this situation, it's fine. You're telling the compiler that you're performing an intentional conversion, an explicit conversion. ### Determine if your conversion is a "widening conversion" or a "narrowing conversion" The term narrowing conversion means that you're attempting to convert a value from a data type that can hold more information to a data type that can hold less information. In this case, you may lose information such as precision (that is, the number of values after the decimal point). An example is converting value stored in a variable of type `decimal` into a variable of type `int`. If you print the two values, you would possibly notice the loss of information. When you know you're performing a narrowing conversion, you need to perform a cast. Casting is an instruction to the C# compiler that you know precision may be lost, but you're willing to accept it. If you're unsure whether you lose data in the conversion, write code to perform a conversion in two different ways and observe the changes. Developers frequently write small tests to better understand the behaviors, as illustrated with the next sample. Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs decimal myDecimal = 1.23456789m; float myFloat = (float)myDecimal; Console.WriteLine($"Decimal: {myDecimal}"); Console.WriteLine($"Float : {myFloat}"); ``` Save your file, and then run your code. You should see output similar to: ```txt Decimal: 1.23456789 Float : 1.2345679 ``` You can observe from the output that casting a `decimal` into a `float` is a narrowing conversion because you're losing precision. ### Performing Data Conversions Earlier, it was stated that a value change from one data type into another could cause a runtime exception, and you should perform data conversion. For data conversions, there are three techniques you can use: - Use a helper method on the variable - Use a helper method on the data type - Use the `Convert` class' methods ### Use `ToString()` to convert a number to a `string` Every data type variable has a `ToString()` method. What the `ToString()` method does depends on how it's implemented on a given type. However, on most primitives, it performs a widening conversion. While this isn't strictly necessary (since you can rely on implicit conversion in most cases) it can communicate to other developers that you understand what you're doing and it's intentional. Here's a quick example of using the `ToString()` method to explicitly convert `int` values into strings. Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs int first = 5; int second = 7; string message = first.ToString() + second.ToString(); Console.WriteLine(message); ``` Save your file, and then run the code, the output should display a concatenation of the two values: ```txt 57 ``` ### Convert a `string` to an `int` using the `Parse()` helper method Most of the numeric data types have a `Parse()` method, which converts a `string` into the given data type. In this case, you use the `Parse()` method to convert two strings into int values, then add them together. Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs string first = "5"; string second = "7"; int sum = int.Parse(first) + int.Parse(second); Console.WriteLine(sum); ``` Save your file, and then run the code, the output should display a sum of the two values: ```txt 12 ``` Take a minute to try to spot the potential problem with the previous code example. What if either of the variables `first` or `second` are set to values that can't be converted to an `int`? An exception is thrown at runtime. The C# compiler and runtime expects you to plan ahead and prevent "illegal" conversions. You can mitigate the runtime exception in several ways. The easiest way to mitigate this situation is by using `TryParse()`, which is a better version of the `Parse()` method. ### Convert a `string` to a `int` using the `Convert` class The `Convert` class has many helper methods to convert a value from one type into another. In the following code example, you convert a couple of strings into values of type `int`. > Note > The code samples in this exercise are designed based on en-US culture setting s, and use a period (`.`) as the decimal separator. Building and running the code with a culture setting that uses a different decimal separators (such as a comma `,`) may give unexpected results or errors. To fix this issue, replace the period decimal separators in the code samples with your local decimal separator (such as `,`). Alternatively, to run a program using the en-US culture setting, add the following code to the top of your program: `using System.Globalization;` and after any other `using` statements add `CultureInfo.CurrentCulture = new CultureInfo("en-US");`. Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs string value1 = "5"; string value2 = "7"; int result = Convert.ToInt32(value1) * Convert.ToInt32(value2); Console.WriteLine(result); ``` Save your file, and then run your code. You should see the following output: ```txt 35 ``` > Note > Why is the method name `ToInt32()`? Why not `ToInt()`? > `System.Int32` is the name of the underlying data type in the .NET Class Library that the C# programming language maps to the keyword `int`. Because the `Convert` class is also part of the .NET Class Library, it is called by its full name, not its C# name. By defining data types as part of the .NET Class Library, multiple .NET languages like Visual Basic, F#, IronPython, and others can share the same data types and the same classes in the .NET Class Library. The `ToInt32()` method has 19 overloaded versions allowing it to accept virtually every data type. you used the `Convert.ToInt32()` method with a string here, but you should probably use `TryParse()` when possible. So, when should you use the `Convert` class? The `Convert` class is best for converting fractional numbers into whole numbers (`int`) because it rounds up the way you would expect. ### Compare casting and converting a `decimal` into an `int` The following example demonstrates what happens when you attempt to cast a `decimal` into an `int` (a narrowing conversion) versus using the `Convert.ToInt32()` method to convert the same `decimal` into an `int`. Delete or use the line comment operator `//` to comment out the code from the previous exercise step, and add the following code: ```cs int value = (int)1.5m; // casting truncates Console.WriteLine(value); int value2 = Convert.ToInt32(1.5m); // converting rounds up Console.WriteLine(value2); ``` Save your file, and then run your code. You should see the following output: ```txt 1 2 ``` #### Casting truncates and converting rounds When you're casting `int value = (int)1.5m;`, the value of the float is truncated so the result is `1`, meaning the value after the decimal is ignored completely. You could change the literal float to `1.999m` and the result of casting would be the same. When you're converting using `Convert.ToInt32()`, the literal float value is properly rounded up to `2`. If you changed the literal value to `1.499m`, it would be rounded down to `1`. ### Recap You covered several important concepts of data conversion and casting: - Prevent a runtime error while performing a data conversion - Perform an explicit cast to tell the compiler you understand the risk of losing data - Rely on the compiler to perform an implicit cast when performing an expanding conversion - Use the `()` cast operator and the data type to perform a cast (for example, `(int)myDecimal`) - Use the `Convert` class when you want to perform a narrowing conversion, but want to perform rounding, not a truncation of information --- ## Exercise ### Examine the TryParse() method When working with data, sometimes, you need to convert string data into a numeric data type. As you learned in the previous unit, because the string data type can hold a non-numeric value, it's possible that performing a conversion from a `string` into a numeric data type causes a runtime error. For example, the following code: ```cs string name = "Bob"; Console.WriteLine(int.Parse(name)); ``` Causes the following exception: ```txt System.FormatException: 'Input string was not in a correct format.' ``` To avoid a format exception, use the TryParse() method on the target data type. ### Use TryParse() The TryParse() method does several things simultaneously: - It attempts to parse a string into the given numeric data type. - If successful, it stores the converted value in an out parameter, explained in following section. - It returns a `bool` to indicate whether the action succeeded or failed. You can use the Boolean return value to take action on the value (like performing some calculation), or display a message if the parse operation was unsuccessful. > Note > In this exercise, you'll use the `int` data type, but a similar `TryParse()` method is available on all numeric data types. #### Out parameters Methods can return a value or return "void" - meaning they return no value. Methods can also return values through `out` parameters, which are defined just like an input parameter, but include the `out` keyword. #### TryParse() a string into an int Delete or use the line comment operator `//` to comment out all of the code from the previous exercises. Update your code in the editor as follows: ```cs string value = "102"; int result = 0; if (int.TryParse(value, out result)) { Console.WriteLine($"Measurement: {result}"); } else { Console.WriteLine("Unable to report the measurement."); } ``` Examine this line of code: ```cs if (int.TryParse(value, out result)) ``` When calling a method with an `out` parameter, you must use the keyword `out` before the variable, which holds the value. The `out` parameter is assigned to the `result` variable in the code `(int.TryParse(value, out result)`. You can then use the value the `out` parameter contains throughout the rest of your code using the variable `result`. The `int.TryParse()` method returns `true` if it successfully converted the `string` variable `value` into an `int`; otherwise, it returns `false`. So, surround the statement in an `if` statement, and then perform the decision logic, accordingly. The converted value is stored in the `int` variable `result`. The `int` variable `result` is declared and initialized before this line of code, so it should be accessible both *inside* the code blocks that belong to the `if` and `else` statements, as well as *outside* of them. The `out` keyword instructs the compiler that the `TryParse()` method doesn't return a value the traditional way only (as a return value), but also communicates an output through this two-way parameter. When you run the code, you should see the following output: ```txt Measurement: 102 ``` #### Use the parsed `int` later in code To demonstrate that the `result` variable that was declared earlier, is populated by the `out` parameter and is also usable later in your code, update your code as follows: ```cs 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}"); ``` You should see the following output: ```txt Measurement: 102 Measurement (w/ offset): 152 ``` Examine the last line of code in the previous sample, `Console.WriteLine($"Measurement (w/ offset): {50 + result}");`, Since the `result` variable is defined outside of the if statement, it can be accessed later in your code. #### Modify the string variable to a value that can't be parsed Lastly, look at the other scenario - where the `TryParse()` is intentionally given a bad value that can't be converted into an int. #### Modify the first line of code, reinitialize the variable `value` to a different value. ```cs string value = "bad"; ``` Also, modify the last line of code to ensure that the result is greater than 0 before showing the second message. ```cs if (result > 0) Console.WriteLine($"Measurement (w/ offset): {50 + result}"); The entire code example should now match the following code: ``` ```cs 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}"); ``` Save your code file, and then run your code. You should get the following result: ```txt Unable to report the measurement. ``` Examine the last two lines of code added in the previous sample. ```cs if (result > 0) Console.WriteLine($"Measurement (w/ offset): {50 + result}"); ``` Since `result` is defined outside of the `if` statement, `result` can be accessed later in your code outside of the code blocks. So then `result` can be checked for a value greater than zero before allowing `result` + offset to be written as output. Checking for a `result` value greater than zero avoids printing an offset value after the `Unable to report the measurement.` message. ### Recap The `TryParse()` method is a valuable tool. Here are few quick ideas to remember. - Use `TryParse()` when converting a string into a numeric data type. - `TryParse()` returns `true` if the conversion is successful, `false` if it's unsuccessful. - Out parameters provide a secondary means of a method returning a value. In this case, the `out` parameter returns the converted value. - Use the keyword `out` when passing in an argument to a method that has defined an `out` parameter. --- ## Exercise ### Complete a challenge to combine string array values as strings and as integers Code challenges reinforce what you've learned and help you gain some confidence before continuing. This module features two code challenges. This first challenge forces you to split up the data depending on its type and either concatenate or add the data accordingly. > Note > The code samples in this exercise are designed based on en-US culture setting s, and use a period (`.`) as the decimal separator. Building and running the code with a culture setting that uses a different decimal separators (such as a comma `,`) may give unexpected results or errors. To fix this issue, replace the period decimal separators in the code samples with your local decimal separator (such as `,`). Alternatively, to run a program using the en-US culture setting, add the following code to the top of your program: `using System.Globalization;` and after any other `using` statements add `CultureInfo.CurrentCulture = new CultureInfo("en-US");`. Select and delete all code lines in the code editor. Optionally, use the line comment operator `//` to comment out all of the code from the previous step. To instantiate a string array, enter the following "starter" code: ```cs string[] values = { "12.3", "45", "ABC", "11", "DEF" }; ``` Create a looping structure that can be used to iterate through each string value in the array `values`. Complete the required code, placing it within the array looping structure code block. It's necessary to implement the following business rules in your code logic: - Rule 1: If the value is alphabetical, concatenate it to form a message. - Rule 2: If the value is numeric, add it to the total. - Rule 3: The result should match the following output: ```txt Message: ABCDEF Total: 68.3 ``` The Program.cs file must be saved before building or running the code. At the Terminal command prompt, to run your code, type `dotnet run` and then press Enter. You should see the following output: ```txt Message: ABCDEF Total: 68.3 ``` > Note > If you see a message saying "Couldn't find a project to run", ensure that the Terminal command prompt displays the expected TestProject folder location. For example: C:\Users\someuser\Desktop\csharpprojects\TestProject> Whether you get stuck and need to peek at the solution or you finish successfully, continue to view a solution to this challenge. --- ## Exercise ### Complete a challenge to output math operations as specific number types Here's a second chance to use what you've learned about casting and conversion to solve a coding challenge. The following challenge helps you to understand the implications of casting values considering the impact of narrowing and widening conversions. Delete or comment out all of the code from the earlier exercise Enter the following "starter" code: ```cs 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}"); ``` Replace the code comments in the starter code with your own code to solve the challenge: - Solve for `result1`: Divide `value1` by `value2`, display the result as an `int` - Solve for `result2`: Divide `value2` by `value3`, display the result as a `decimal` - Solve for `result3`: Divide `value3` by `value1`, display the result as a `float` Solve the challenge so that your output resembles: ```txt 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 ``` The Program.cs file must be saved before building or running the code. At the Terminal command prompt, to run your code, type `dotnet run` and then press Enter. You should see the following output: ```txt 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 ``` --- ## Summary Your goal was to use several different techniques to change the data type of a given value. You used *implicit conversion*, relying on the C# compiler to perform *widening conversions*. When the compiler was unable to perform an implicit conversion, you used explicit conversions. You used the `ToString()` method to explicitly convert a numeric data type into a `string`. When you needed to perform `narrowing conversions`, you used several different techniques. You used the casting operator `()` when the conversion could be made safely and were willing to accept truncation of values after the decimal. And you used the `Convert()` method when you wanted to perform a conversion and use common rounding rules when performing a narrowing conversion. Finally, you used the `TryParse()` methods when the conversion from a `string` to a numeric data type could potentially result in a data type conversion exception. Without this wealth of options, it would be difficult to work in a typed programming language. Fortunately, this well executed system of types, conversion, and casting can be harnessed to build error free applications. ### Resources - [Casting and type conversions (C# programming guide)](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions) - [Built-in types (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types) - [Default values of C# types (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/default-values)