Conversions between Data Types






Conversions between Data Types

Given the thousands of types predefined in the various CLI implementations and the unlimited number of types that code can define, it is important that types support conversion from one to another where it makes sense. The most common type of conversion is casting.

Consider the conversion between two numerical types: converting from a variable of type long to a variable of type int. A long type can contain values as large as 9,223,372,036,854,775,808; however, the maximum size of an int is 2,147,483,647. As such, that conversion could result in a loss of datafor example, if the variable of type long contains a value greater than the maximum size of an int. Any conversion that could result in a loss of data or an exception because the conversion failed requires an explicit cast. Conversely, a casting operation that will not lose precision and will not throw an exception regardless of the operand types is an implicit cast.

Explicit Cast

In C#, you cast using the cast operator. By specifying the type you would like the variable converted to within parentheses, you acknowledge that if an explicit cast is occurring, there may be a loss of precision and data, or an exception may result. The code in Listing 2.18 converts a long to an int and explicitly tells the system to attempt the operation.

Explicit Cast Example

With the cast operator, the programmer essentially says to the compiler, "Trust me, I know what I am doing. I know that the conversion could possibly not fit but I am willing to take the chance." Making such a choice will cause the compiler to allow the conversion. However, with an explicit conversion, there is still a chance that an error, in the form of an exception, might occur at runtime if the data does not convert successfully. It is, therefore, the programmer's responsibility to ensure the data will successfully convert, or else to provide the necessary code logic when it doesn't.

Advanced Topic: Checked and Unchecked Conversions

C# provides special keywords for marking a code block to indicate what should happen if the target data type is too small to contain the assigned data. By default, if the target data type cannot contain the assigned data, then the data will truncate during assignment. For an example, see Listing 2.19.

Overflowing an Integer Value

public class Program
{
  public static void Main()
  {
       // int.MaxValue equals 2147483647
       int n = int.MaxValue;
       n = n + 1 ;
       System.Console.WriteLine(n);
  }
}

Output 2.14 shows the results.

Output 2.14.

-2147483648

Listing 2.19 writes the value -2147483648 to the console. However, placing the code within a checked block, or using the checked option when running the compiler, will cause the runtime to throw an exception of type System.OverflowException. The syntax for a checked block uses the checked keyword, as shown in Listing 2.20.

A Checked Block Example

public class Program
{
  public static void Main()
  {
      checked                                              
      {                                                    
          // int.MaxValue equals 2147483647
          int n = int.MaxValue;
          n = n + 1 ;
          System.Console.WriteLine(n);
      }                                                    
  }
}

Output 2.15 shows the results.

Output 2.15.

Unhandled Exception: System.OverflowException: Arithmetic operation
resulted in an overflow at Program.Main() in ...Program.cs:line 12

The result is that an exception is thrown if, within the checked block, an overflow assignment occurs at runtime.

The C# compiler provides a command-line option for changing the default checked behavior from unchecked to checked. C# also supports an unchecked block that truncates the data instead of throwing an exception for assignments within the block (see Listing 2.21).

An Unchecked Block Example

using System;

public class Program
{
  public static void Main()
  {
      unchecked                                            
      {                                                  
           // int.MaxValue equals 2147483647
           int n = int.MaxValue;
           n = n + 1 ;
           System.Console.WriteLine(n);
     }                                                   
  }
}

Output 2.16 shows the results.

Output 2.16.

-2147483648

Even if the checked option is on during compilation, the unchecked keyword in the preceding code will prevent the runtime from throwing an exception during execution.


You cannot convert any type to any other type simply because you designate the conversion explicitly using the cast operator. The compiler will still check that the operation is valid. For example, you cannot convert a long to a bool. No such cast operator is defined, and therefore, the compiler does not allow such a cast.

Language Contrast: Converting Numbers to Booleans

It may be surprising that there is no valid cast from a numeric type to a Boolean type, since this is common in many other languages. The reason no such conversion exists in C# is to avoid any ambiguity, such as whether 1 corresponds to true or false. More importantly, as you will see in the next chapter, this also reduces the chance of using the assignment operator in place of the equality operator (avoiding if(x=42){...} when if(x==42){...} was intended, for example).


Implicit Cast

In other instances, such as going from an int type to a long type, there is no loss of precision and there will be no fundamental change in the value of the type. In these cases, code needs only to specify the assignment operator and the conversion is implicit. In other words, the compiler is able to determine that such a conversion will work correctly. The code in Listing 2.22 converts from an int to a long by simply using the assignment operator.

Not Using the Cast Operator for an Implicit Cast

int intNumber = 31416;
long longNumber = intNumber;

Even when no explicit cast operator is required (because an implicit conversion is allowed), it is still possible to include the cast operator (see Listing 2.23).

Using the Cast Operator for an Implicit Cast

int intNumber = 31416;
long longNumber = (long) intNumber;

Type Conversion without Casting

Neither an implicit nor an explicit cast is defined from a string to a numeric type, so methods such as Parse() are required. Each numeric data type includes a Parse() function that enables conversion from a string to the corresponding numeric type. Listing 2.24 demonstrates this call.

Listing 2.24. Using int.Parse() to Convert a string to a Numeric Data Type

string text = "9.11E-31";
float kgElectronMass = float.Parse(text);

Another special type is available for converting one type to the next. The type is System.Convert and an example of its use appears in Listing 2.25.

Listing 2.25. Type Conversion Using System.Convert

stringmiddleCText = "278.4375";
double middleC = System.Convert.ToDouble(middleCText);
bool boolean = System.Convert.ToBoolean(middleC);

System.Convert supports only a predefined number of types and it is not extensible.

Furthermore, all types support a ToString() method that can be used to provide a string representation of a type. Listing 2.26 demonstrates how to use this method. The resulting output is shown in Output 2.17.

Listing 2.26. Using ToString() to Convert to a string

bool boolean = true;
string text = boolean.ToString();
// Display "True"
System.Console.WriteLine(text);

Output 2.17.

True

For the majority of types, the ToString() method will return the name of the data type rather than a string representation of the data. The string representation is returned only if the type has an explicit implementation of ToString(). One last point to make is that it is possible to code custom conversion methods, and many such methods are available for classes in the runtime.

Advanced Topic: tryParse()

In C# 2.0, all the numeric primitive types include a static tryParse() method. (In C# 1.0, only double includes such a method.) This method is very similar to the Parse() method, except that instead of throwing an exception if the conversion fails, the tryParse() method returns false, as demonstrated in Listing 2.27.

Listing 2.27. Using TRyParse() in Place of an Invalid Cast Exception

double number;
string input;

System.Console.Write("Enter a number: ");
input = System.Console.ReadLine();
if (double.TryParse(input, out number))                          
{                                                                
    // Converted correctly, now use number                       

    // ...                                                       
}                                                                
else                                                             
{
   System.Console.WriteLine(
       "The text entered was not a valid number.");
}

Output 2.18 shows the results of Listing 2.27.

Output 2.18.

Enter a number: forty-two
The text entered was not a valid number.

The resulting value the code parses from the input string is returned via an out parameterin this case, number.

The key difference between Parse() and tryParse() is the fact that TRyParse() won't throw an exception if it fails. Frequently, the conversion from a string to a numeric type depends on a user entering the text. It is expected, in such scenarios, that the user will enter invalid data that will not parse successfully. By using TRyParse() rather than Parse(), you can avoid throwing exceptions in expected situations. (The expected situation in this case is that the user will enter invalid data.)




 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows