[ Team LiB ] |
Recipe 3.13 Converting Between Simple Types in a Language Agnostic MannerProblemYou need to convert between any two of the following types: bool, char, sbyte, byte, short, ushort, int, uint, long, ulong, float, double, decimal, DateTime, and string. Different languages sometimes handle specific conversions differently; you need a way to perform these conversions in a consistent manner across all .NET languages. One situation where this recipe is needed is when VB.NET and C# components communicate within the same application. SolutionDifferent languages sometimes handle casting of larger numeric types to smaller numeric types differently—these types of casts are called narrowing conversions. For example consider the following Visual Basic .NET (VB.NET) code which casts a Single to an Integer: ' Visual Basic .NET Code: Dim initialValue As Single Dim finalValue As Integer initialValue = 13.499 finalValue = CInt(initialValue) Console.WriteLine(finalValue.ToString( )) initialValue = 13.5 finalValue = CInt(initialValue) Console.WriteLine(finalValue.ToString( )) initialValue = 13.501 finalValue = CInt(initialValue) Console.WriteLine(finalValue.ToString( )) This code outputs the following: 13 14 14 Notice that using the CInt cast in VB.NET uses the fractional portion of the number to round the resulting number. Now let's convert this code to C# using the explicit casting operator: // C# Code: float initialValue = 0; int finalValue = 0; initialValue = (float)13.499; finalValue = (int)initialValue; Console.WriteLine(finalValue.ToString( )); initialValue = (float)13.5; finalValue = (int)initialValue; Console.WriteLine(finalValue.ToString( )); initialValue = (float)13.501; finalValue = (int)initialValue; Console.WriteLine(finalValue.ToString( )); This code outputs the following: 13 13 13 Notice that the resulting value was not rounded. Instead, the C# casting operator simply truncates the fractional portion of the number. Consistently casting numeric types in any language can be done through the static methods on the Convert class. The previous C# code can be converted to use the ToInt32 method: // C# Code: finalValue = Convert.ToInt32((float)13.449); Console.WriteLine(finalValue.ToString( )); finalValue = Convert.ToInt32((float)13.5); Console.WriteLine(finalValue.ToString( )); finalValue = Convert.ToInt32((float)13.501); Console.WriteLine(finalValue.ToString( )); This code outputs the following: 13 14 14 DiscussionAll conversions performed using methods on the Convert class are considered to be in a checked context in C#. VB.NET does not have the concept of a checked or unchecked context, so all conversions are considered to be in a checked context—an unchecked context cannot be created in VB.NET. An OverflowException will be thrown in a checked context when a narrowing conversion results in a loss of information. This exception is never thrown in an unchecked context when a narrowing conversion results in a loss of information. The various conversion methods are listed in Table 3-2.
Converting between any of the data types listed in Table 3-2 is a simple matter. All of the listed methods are static and exist on the Convert class. Converting one type to another is performed by first choosing the correct method on the Convert class. This method will be named after the type you are converting to (e.g., if you are converting to a char type, the method name would be ToChar). Next, you need to pass the type that will be casted as the parameter to the Convert method. Finally, set a variable of the resultant cast type equal to the return value of the Convert method. The following code converts the value in variable Source—defined as a short that contains a number between 0 and 9—to a char type. This char value is then returned by the Convert method and assigned to the variable destination. The variable destination must be defined as a char: destination = Convert.ToChar(source); There are cases in which conversions will do nothing. Converting from one type to that same type will do nothing except return a result that is equivalent to the source variable's value. Take, for example, using the Convert.ToInt32 method to convert a source variable of type Int32 to a destination variable of type Int32. This method takes the value obtained from the source variable and places it in the destination variable. Some conversions cause exceptions to occur because there is no clear way of converting between the two types; these attempted conversions are listed in Table 3-3. Because some conversions might or might not throw an exception—such as converting from an sbyte to a byte—it is good programming practice to enclose the static conversion method within a try/catch block. The following code wraps a conversion between numeric types in a try/catch block: try { finalValue = Convert.ToInt32(SomeFloat); } catch(OverflowException oe) { // Handle narrowing conversions that result in a loss // of information here. } catch(InvalidCastException ice) { // Handle casts that cannot be performed here. } The following code wraps a conversion from a string type to an Int32 in a try/catch block: try { finalValue = Convert.ToInt32(SomeString); } catch(OverflowException oe) { // Handle narrowing conversions that result in a loss // of information here. } catch(ArgumentException ae) { // Handle nulls passed into the Convert method here. } catch(FormatException fe) { // Handle attempts to convert a string that does not contain // a value that can be converted to the destination type here. } catch(Exception e) { // Handle all other exceptions here. }
Notice that the string type can be converted to any type, and that any type may be converted to a string type—assuming that the source string is not null and conforms to the destination type's range. The most insidious problems can occur when a larger type is converted to a smaller type in an unchecked context; the potential exists for information to be lost. Code runs in an unchecked context if the conversion is contained in an unchecked block or if the /checked compiler option is set to false (by default, this compiler option is set to false in both debug and release builds). An example of code contained in an unchecked block is as follows: short destination = 0; int source = Int32.MaxValue; unchecked(destination = (short)source); or: unchecked { short destination = 0; int source = Int32.MaxValue; destination = (short)source; } A checked context is when the conversion is contained in a checked block or if the /checked compiler option is set to true. An example of code contained in a checked block is as follows: short destination = 0; int source = Int32.MaxValue; checked(destination =(short)source); or: checked { short destination = 0; int source = Int32.MaxValue; destination = (short)source; } This code throws an OverflowException exception if any loss of information would occur. This allows the application to be notified of the overflow condition and to handle it properly. The Convert method is always considered to operate in a checked context, even when no other type of checked context wraps the code performing the conversion. See AlsoSee the "checked" keyword, "unchecked" keyword, "Checked and Unchecked," and "Convert Class" topics in the MSDN documentation. |
[ Team LiB ] |