DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 3.13 Converting Between Simple Types in a Language Agnostic Manner

Problem

You 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.

Solution

Different 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

Discussion

All 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.

Table 3-2. Conversion methods on the Convert class

Method

Use

ToBoolean

Convert a type to a bool

ToChar

Convert a type to a char

ToString

Convert a type to a string

ToDateTime

Convert a type to a DateTime

ToInt16

Convert a type to a short

ToInt32

Convert a type to an int

ToInt64

Convert a type to a long

ToUInt16

Convert a type to a ushort

ToUInt32

Convert a type to a uint

ToUInt64

Convert a type to a ulong

ToByte

Convert a type to a byte

ToSByte

Convert a type to an sbyte

ToSingle

Convert a type to a float

ToDecimal

Convert a type to a decimal

ToDouble

Convert a type to a double

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.
}

Table 3-3. Cases where a Source to Destination type conversion throws an exception

Destination

Source

Exception type

bool

charDateTime

InvalidCastException

byte

DateTime

InvalidCastException

char

boolDateTimedecimaldoublefloat

InvalidCastException

DateTime

boolbytesbytechardecimaldoubleshortintlongushortuintulongobjectfloat

InvalidCastException

decimal

charDateTime

InvalidCastException

double

charDateTime

InvalidCastException

short

DateTime

InvalidCastException

int

DateTime

InvalidCastException

long

DateTime

InvalidCastException

sbyte

DateTime

InvalidCastException

float

charDateTime

InvalidCastException

ushort

DateTime

InvalidCastException

uint

DateTime

InvalidCastException

ulong

DateTime

InvalidCastException

byte

sbyte

OverFlowException (if Source is out of the range of Destination)

sbyte

byteushortuintulong

OverFlowException (if Source is out of the range of Destination)

short

bytesbyteushort

OverFlowException (if Source is out of the range of Destination)

ushort

bytesbyteshort

OverFlowException (if Source is out of the range of Destination)

int

bytesbyteshortushortuint

OverFlowException (if Source is out of the range of Destination)

uint

bytesbyteshortushortint

OverFlowException (if Source is out of the range of Destination)

long

bytesbyteshortushortintuintulong

OverFlowException (if Source is out of the range of Destination)

ulong

bytesbyteshortushortintuintlong

OverFlowException (if Source is out of the range of Destination)

decimal

bytesbyteshortushortintuintlongulong

OverFlowException (if Source is out of the range of Destination)

float

bytesbyteshortushortintuintlongulong

OverFlowException (if Source is out of the range of Destination)

double

bytesbyteshortushortintuintlongulong

OverFlowException (if Source is out of the range of Destination)

Any type

string

ArgumentException (if source string is null) orFormatException (if source string represents an invalid value for the Destination type)

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 Also

See the "checked" keyword, "unchecked" keyword, "Checked and Unchecked," and "Convert Class" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section