In programming, it is common to assign one type of variable to another. For example, you might want to assign an int value to a float variable, as shown here:
int i; float f; i = 10; f = i; // assign an int to a float
When compatible types are mixed in an assignment, the value of the right side is automatically converted to the type of the left side. Thus, in the preceding fragment, the value in i is converted into a float and then assigned to f. However, because of C#’s strict type-checking, not all types are compatible, and thus, not all type conversions are implicitly allowed. For example, bool and int are not compatible. Fortunately, it is still possible to obtain a conversion between incompatible types by using a cast. A cast performs an explicit type conversion. Both automatic type conversion and casting are examined here.
Automatic Conversions
When one type of data is assigned to another type of variable, an automatic type conversion will take place if
The two types are compatible.
The destination type has a range that is greater than the source type.
When these two conditions are met, a widening conversion takes place. For example, the int type is always large enough to hold all valid byte values, and both int and byte are integer types, so an automatic conversion can be applied. An automatic type conversion is also called an implicit conversion.
For widening conversions, the numeric types, including integer and floating-point types, are compatible with each other. For example, the following program is perfectly valid since long to double is a widening conversion that is automatically performed.
// Demonstrate automatic conversion from long to double. using System; class LtoD { public static void Main() { long L; double D; L = 100123285L; D = L; Console.WriteLine("L and D: " + L + " " + D); } }
Although there is an automatic conversion from long to double, there is no automatic conversion from double to long since this is not a widening conversion. Thus, the following version of the preceding program is invalid:
// *** This program will not compile. *** using System; class LtoD { public static void Main() { long L; double D; D = 100123285.0; L = D; // Illegal!!! Console.WriteLine("L and D: " + L + " " + D); } }
In addition to the restrictions just described, there are no automatic conversions between decimal and float or double, or from the numeric types to char or bool. Also, char and bool are not compatible with each other.
Casting Incompatible Types
Although the automatic type conversions are helpful, they will not fulfill all programming needs, because they apply only to widening conversions between compatible types. For all other cases, you must employ a cast. A cast is an instruction to the compiler to convert one type into another. Thus, it requests an explicit type conversion. A cast has this general form:
(target-type) expression
Here, target-type specifies the desired type to convert the specified expression to. For example, if you want the type of the expression x/y to be int, you can write
double x, y; // ... int i = (int) (x / y) ;
Here, even though x and y are of type double, the cast converts the outcome of the expression to int. The parentheses surrounding x / y are necessary. Otherwise, the cast to int would apply only to the x, and not to the outcome of the division. The cast is necessary here because there is no automatic conversion from double to int.
When a cast involves a narrowing conversion, information might be lost. For example, when casting a long into an int, information will be lost if the long’s value is greater than the range of an int, because its high-order bits are removed. When a floating-point value is cast to an integer type, the fractional component will also be lost due to truncation. For example, if the value 1.23 is assigned to an integer, the resulting value will simply be 1. The 0.23 is lost.
The following program demonstrates some type conversions that require casts. It also shows some situations in which the casts cause data to be lost.
// Demonstrate casting. using System; class CastDemo { public static void Main() { double x, y; byte b; int i; char ch; uint u; short s; long l; x = 10.0; y = 3.0; // cast an int into a double i = (int) (x / y); // cast double to int, fractional component lost Console.WriteLine("Integer outcome of x / y: " + i); Console.WriteLine(); // cast an int into a byte, no data lost i = 255; b = (byte) i; Console.WriteLine("b after assigning 255: " + b + " -- no data lost."); // cast an int into a byte, data lost i = 257; b = (byte) i; Console.WriteLine("b after assigning 257: " + b + " -- data lost."); Console.WriteLine(); // cast a uint into a short, no data lost u = 32000; s = (short) u; Console.WriteLine("s after assigning 32000: " + s + " -- no data lost."); // cast a uint into a short, data lost u = 64000; s = (short) u; Console.WriteLine("s after assigning 64000: " + s + " -- data lost."); Console.WriteLine(); // cast a long into a uint, no data lost l = 64000; u = (uint) l; Console.WriteLine("u after assigning 64000: " + u + " -- no data lost."); // cast a long into a uint, data lost l = -12; u = (uint) l; Console.WriteLine("u after assigning -12: " + u + " -- data lost."); Console.WriteLine(); // cast an int into a char b = 88; // ASCII code for X ch = (char) b; Console.WriteLine("ch after assigning 88: " + ch); } }
The output from the program is shown here:
Integer outcome of x / y: 3 b after assigning 255: 255 -- no data lost. b after assigning 257: 1 -- data lost. s after assigning 32000: 32000 -- no data lost. s after assigning 64000: -1536 -- data lost. u after assigning 64000: 64000 -- no data lost. u after assigning -12: 4294967284 -- data lost. ch after assigning 88: X
Let’s look at each assignment. The cast of (x / y) to int results in the truncation of the fractional component, and information is lost.
No loss of information occurs when b is assigned the value 255 because a byte can hold the value 255. However, when the attempt is made to assign b the value 257, information loss occurs because 257 exceeds a byte’s range. In both cases the casts are needed because there is no automatic conversion from int to byte.
When the short variable s is assigned the value 32,000 through the uint variable u, no data is lost because a short can hold the value 32,000. However, in the next assignment, u has the value 64,000, which is outside the range of a short, and data is lost. In both cases the casts are needed because there is no automatic conversion from uint to short.
Next, u is assigned the value 64,000 through the long variable l. In this case, no data is lost because 64,000 is within the range of a uint. However, when the value −12 is assigned to u, data is lost because a uint cannot hold negative numbers. In both cases the casts are needed because there is no automatic conversion from long to uint.
Finally, no information is lost, but a cast is needed when assigning a byte value to a char.
No comments:
Post a Comment