Converting Types

Converting Types

The C# programming language puts a high value on type safety. Every value in a C# program has a specific type, and each type has a unique contract to which the type adheres. When a value is assigned to a variable that has a different type, a type conversion is required.

Type conversion is used to migrate values from one type to another. When a new type is defined, such as the BattingAverage type in the previous section, type conversion is often used to integrate the new type into existing code. For example, later in this section we’ll add some type conversion operators to simplify converting a BattingAverage object to a string or float value. Understanding how C# type conversion works can make your types much easier to use.

Performing Implicit Conversions

Some type conversions are inherently safe and are allowed to occur automatically, without any conversion code required. When you’re converting between scalar types, some conversions are guaranteed to succeed. For example, scalar types such as short, int, and long differ only in the range of values they can store. A short value can always be stored in an int, and an int can always be stored in a long, without writing any conversion code, as shown here:

short s = 32767;
int n = s;

One characteristic of these implicit conversions is that they are inherently asymmetrical, meaning that you can implicitly store a short value in an int, but a conversion in the other direction can potentially result in the loss of data, and can’t be done implicitly by the compiler.

Performing Explicit Conversions

Conversions that result in a loss of data require you to write code that explicitly demands that a conversion take place. Examples of conversions that must be managed explicitly are a conversion that results in a loss of precision or a conversion to a scalar type with a smaller range.

Many times, a conversion that might result in a loss of data is actually harmless. For example, a conversion from a double to a float results in a loss of precision; in your application, however, the degradation in precision might be a harmless conversion. A value stored as a long can be safely converted to an int if you know that its value will always fall into the range permitted for an int.

The brute-force way to explicitly convert a value from one type to a type that might result in a loss of data is to use the cast operator. As discussed earlier in this chapter in the section “Using Other Operators,” to use the cast operator, you enclose the new type in parentheses and place it to the left of the value to be cast, as shown here:

int longVal = 32767;
short s = (short)longVal;

Another method that can be used to convert types is to use the System.Convert class, as discussed in the next section.

String conversion can be accomplished in multiple ways, one of which is to override the ToString method. The object base class implements a default version of ToString, and most types override that implementation and provide a new version of ToString that converts the object’s value to a string. For the BattingAverage type, the ToString method has been overridden first to calculate the batting average and then to format the text as a string in the traditional batting average format, with three digits to the left of the decimal point, as shown here:

// Convert the batting average to a string.
public override string ToString()
    float average = Average();
    string result = string.Format("{0:#.000}", average);
    return result;          

Overriding the Object.ToString method simplifies the use of the Batting­Average type when you’re formatting text. For example, when a BattingAverage object is passed to the Console.WriteLine method, the Console class will use the ToString method to retrieve the text that’s sent to the console. With the following code, a batting average of .333 is displayed to the user:

// 30 at bats, 10 hits
BattingAverage ba = new BattingAverage(30, 10);
Using the Convert Class

The Convert class offers a convenient way to convert values to different types, even if the types are unrelated. The Convert class has an added feature: the current culture settings are respected, so symbols such as the decimal separator are formatted correctly.

All methods in the Convert class are static, so you never need to create a Convert object. The following code uses the Convert class to convert a string value to an int value:

string stringValue = "1542";
int intValue = Convert.ToInt32(stringValue);

Each of the ToXxxx methods is used to convert a value to one of the primitive .NET types, where Xxxx is a .NET type name such as Int16, Int32, or String. The class overloads these methods for ease of use (the ToInt32 method, for example, can convert several different types to an Int32), with some methods offering dozens of overloads.

Performing User-Defined Conversions

A user-defined conversion is a type-conversion method supplied by a class or structure that defines how an object of that type can be converted to another type. In C#, user-defined conversions serve a role similar to that of the C++ cast operator, but they allow more flexibility. A user-defined conversion can be defined as either implicit or explicit. Implicit conversions can occur automatically when a parameter is passed to a method or during an assignment. Explicit conversions take place only if the cast operator is used. If a conversion can result in a loss of data, you should define the conversion as explicit.

All conversions must abide by the following rules:

  • A user-defined conversion isn’t allowed to convert a type to the same type, or to a subclass or superclass of the type.

  • User-defined conversions must be to or from the type defining the conversion.

  • Conversion can’t be attempted to or from an interface or the object type.

These rules help provide predictable behavior for user-defined conversions as well as prevent interference with built-in conversions.

User-defined conversion operators accept a single type as a parameter. An explicit user-defined conversion for the BattingAverage type to float looks like this:

public static explicit operator float(BattingAverage avg)
    return avg.Average();

To define an implicit conversion, simply use the implicit keyword instead of explicit as was done in this example. Here the choice of an implicit or explicit conversion is a judgment call, as the conversion doesn’t actually cause any data loss. However, because an implicit conversion would allow the Batting­Average type to be substituted for the float type for any method parameters or assignment, the preceding code requires the caller to use a cast operator for the conversion.

Part III: Programming Windows Forms