Don't sweat C# type conversions

To kick off his column for C# beginners, Harold Davis walks through type conversions, or casting, in .NET. He explains why you should care about casting and discusses the differences between casting in C# and VB.NET.

C# is a strongly typed language, which means that all variables have a type that must be declared, and that the compiler verifies the type consistency of expressions. Expressions are always either built-in C# types or user-defined types.

For programmers coming from a weakly typed language such as Visual Basic 6, strong typing and the programmatic type conversions it mandates are among the most difficult things about working in C#. But cheer up: Working with type conversion is not brain surgery. After reading this two-part article, you should be able to zip through most issues involving C# type conversion.

This first installment will cover:
  • Strong typing and its rationale.
  • How VB.NET differs from C#.NET.
  • The various possible type conversion techniques.
  • Implicit conversion (also called implicit casting).
  • How the various conversion tools, including explicit conversion, differ from each other.

In the second installment, I’ll offer a closer look at how explicit conversion, or casting, works. I’ll also discuss the as operator, the conversion methods that are members of the System.Convert class, and the ToString method.

Understanding strong typing
Why would a development environment bother with strong typing, since it seems to be such a bother? When working in a strongly typed environment, you need to be clear about the type of information that will be stored in a variable. Strong typing makes the developer adhere to good programming discipline and enforces clarity about the contents of variables. It also prevents bugs that can occur when a weakly typed compiler “makes a mistake” about the kind of value in a type—and incorrectly converts it.

To put this another way, weak typing allows a programmer to be lazy—by not clearly thinking about the types of variables and not specifying the appropriate conversion if there is any possible doubt. In this scenario, the compiler guesses what the programmer meant, most of the time correctly, but sometimes introducing errors.

The extra accuracy introduced by strong typing means more work for the programmer. You must explicitly declare all variables (a good practice even in weakly typed environments), and you must pay close attention every time your code converts a value of one type to another. In many cases, this means providing explicit conversion guidance to the compiler using casting or a conversion method. We'll be taking a detailed look at explicit conversion in the second part of this article.

VB.NET compared to C#
Although VB6 is a weakly typed language, strong typing is optional in VB.NET. To make VB.NET strongly typed in much the way that C#.NET is, you run VB.NET with the compiler option Strict set to On. You can do this by adding the statement Option Strict On at the beginning of each code module or on a per-project basis by selecting Build under Common Properties in the project's Property Pages dialog box (Figure A).

Figure A

The VB.NET default is to run with weak typing. So if you run the following code in VB.NET without enabling Option Strict, the program will run without syntax errors:
Dim theFloat As Double = 3.5
Dim X As Integer = 2
X = X + theFloat

The value of theFloat will be rounded up to 4 when it is added and assigned to the integer X. Then, in the message box statement, the integer argument X will be automatically converted to a string type, and the value 6 will be displayed.

This is convenient if it is what you want to have happen. But it's a possible source of confusion in more complex programs if you are not counting on the round-up. Adding 3.5 and 2 and getting 6 as the integer result is not unreasonable, although it may not be what you expect. But adding 2.5, 3.5, and 2 and getting 9—which is what would happen in VB without Option Strict—is pretty weird (8 would be a better result).

The comparable code in C#:
double theFloat = 3.5;
int X = 2;
X = X + theFloat;

will not compile due to several conversion-related syntax errors, as shown in Figure B.

Figure B
Can’t compile error

You can correct the C# code easily enough by casting the double-type variable theFloat to int and using the ToString method to display the contents of the variable X in the message box:
double theFloat = 3.5;
int X = 2;
X = X + (int) theFloat;

This code compiles and runs without any syntax errors. It also produces different results than the VB code does, truncating theFloat to 3 and displaying 5 as the result. The (int) cast has taken the whole-number portion of theFloat variable.

When using a conversion function to perform the round-off operation you’d normally expect when converting 3.5 to an integer value (e.g., 4), you need to use the explicit Convert.ToInt16 method:
double theFloat = 3.5;
int X = 2;
X = X + Convert.ToInt16(theFloat);


Type conversion scenarios
The comparison of weakly typed conversion in VB.NET and strongly typed conversion in C#—and the different values obtained in the example I just showed you—demonstrate the importance of getting type conversion right. It’s your job as a programmer in C# to stage-manage conversions from one type to another.

There are three general ways to convert from one type to another:
  • Implicit conversion (detailed in the next section)
  • Explicit conversion via casting (explained in the next installment)
  • Use of a conversion method (explained in the next installment)

Provided that there is no way that the conversion will lose information, C# will implicitly convert for you without any special syntax. For example, C# will automatically go from integer to long for you (but not from long to integer).

Implicit conversions must be guaranteed to succeed, while explicit conversions are needed if either:
  • Runtime exigencies determine whether the conversion will succeed.
  • Data might be lost during conversion.

If you explicitly convert using casting, you're responsible for making sure that the results are what you anticipate and don’t lead to any unexpected runtime errors. You should anticipate testing your programs with values that will uncover any possible runtime conversion errors in explicit casts and conversions.

Implicit conversion
Table A shows the available implicit built-in type conversions in C#.
Table A
Implicit conversions for simple C# types
Type (conversion from) Legal implicit conversion to
sbyte short, int, long, float, double, or decimal
byte short, ushort, int, uint, long, ulong, float, double, or decimal
short int, long, float, double, or decimal
ushort int, uint, long, ulong, float, double, or decimal
int long, float, double, or decimal
uint long, ulong, float, double, or decimal
long float, double, or decimal
char ushort, int, uint, long, ulong, float, double, or decimal
float double
ulong float, double, or decimal
Implicit conversions for simple C# types

These implicit conversions are never legal:
  • There are no allowable implicit conversions from Boolean, double, or decimal types.
  • There are no implicit conversions allowed to the char type.
  • There are no implicit conversions allowed between the floating-point types and the decimal type.

Implicit conversions are also possible for more complex reference types. Generally, when you eyeball two reference types to see whether you can do an implicit conversion, you should be asking the same question as with simple type conversions: Can I guarantee the success of the operation without data loss?

Some rules do apply to reference-type implicit conversions. For example, any reference type can be implicitly converted to object. And any derived class can be implicitly converted to the class it was derived from.

Harold Davis is the author of Visual Basic .NET Programming and Visual C# .NET Programming, both published by Sybex, and many other books about programming.


Editor's Picks