Throughout this tutorial, you've seen many different types, including those that are part of C# and custom designed types. If you've taken the samples and worked on them yourself, extending and writing your own programs, you are likely to have experienced errors associated with type. For example, you can't assign a double to an int without using a cast operator to perform the conversion. Another feature of C# concerns the semantic differences between reference and value types. Such problems should make you wonder why this is so and that's what this lesson is for. Here are the objectives for this lesson:
- Understand the need for type safety
- See how to convert one type to another
- Learn about reference types
- Learn about value types
- Comprehend the semantic differences between reference and value types
Why Type Safety?
In untyped languages, such as scripting languages, you can assign one variable to another and the compiler/interpreter will use an intelligent algorithm to figure out how the assignment should be done. If the assignment is between two variables of the same type, all is good. However, if the assignment is between different types, you could have serious problems.For example, if you assigned an int value to a float variable it would convert okay because the fractional part of the new float would just be zero. However, if you went the other way and assigned a float value to an int variable, that would most likely be a problem. You would lose all of the precision of the original float value. Consider the damage that could be caused if the float value represented a chemical ingredient, an engineering measurement, or a financial value. Finding such an error would be difficult and particularly expensive, especially if the error didn't show up until your application was in production (already being used by customers).
Using the Cast Operator for Conversions
In Lesson 02: Operators, Types, and Variables, you learned about C# types and operators. It explained the size and precision of the various types and there is a list of available operators. The cast operator, (x), is listed first as a primary operator in Table 2-4. When you must convert a type that doesn't fit, it must be done via what is called an explicit conversion, which uses the cast operator. Listing 22-1 has an example of an implicit conversion, which doesn't require the cast operator, and an explicit conversion.Listing 22-1. Cast Operators
using System; class Program { static void Main() { float lengthFloat = 7.35f; // lose precision - explicit conversion int lengthInt = (int)lengthFloat; // no problem - implicit conversion double lengthDouble = lengthInt; Console.WriteLine("lengthInt = " + lengthInt); Console.WriteLine("lengthDouble = " + lengthDouble); Console.ReadKey(); } }Here's the output:
lengthInt = 7 lengthDouble = 7Since a float, lengthFloat, has a fractional part but an int, lengthInt, doesn't; the types aren't compatible. Because of type safety, C# won't allow you to assign lengthFloat directly to lengthInt, which would be dangerous. For your protection, you must use a cast operator, (int), to force the explicit conversion of lengthFloat to lengthInt. In the output, you can see that lengthInt is 7, showing that it lost the fractional part of the 7.35f value from lengthFloat.
The assignment from lengthInt to lengthDouble is safe because a double is 64-bit and an int is 32-bit, meaning that you won't lose information. Therefore, the conversion is implicit, meaning that you can perform the assignment without the cast operator.
Understanding Reference Types
Reference type variables are named appropriately (reference) because the variable holds a reference to an object. In C and C++, you have something similar that is called a pointer, which points to an object. While you can modify a pointer, you can't modify the value of a reference - it simply points at the object in memory.An important fact you need to understand is that when you are assigning one reference type variable to another, only the reference is copied, not the object. The variable holds the reference and that is what is being copied. Listing 22-2 shows how this works.
Listing 22-2. Reference Type Assignment
using System; class Employee { private string m_name; public string Name { get { return m_name; } set { m_name = value; } } } class Program { static void Main() { Employee joe = new Employee(); joe.Name = "Joe"; Employee bob = new Employee(); bob.Name = "Bob"; Console.WriteLine("Original Employee Values:"); Console.WriteLine("joe = " + joe.Name); Console.WriteLine("bob = " + bob.Name); // assign joe reference to bob variable bob = joe; Console.WriteLine(); Console.WriteLine("Values After Reference Assignment:"); Console.WriteLine("joe = " + joe.Name); Console.WriteLine("bob = " + bob.Name); joe.Name = "Bobbi Jo"; Console.WriteLine(); Console.WriteLine("Values After Changing One Instance:"); Console.WriteLine("joe = " + joe.Name); Console.WriteLine("bob = " + bob.Name); Console.ReadKey(); } }Here's the output:
Original Employee Values: joe = Joe bob = Bob Values After Reference Assignment: joe = Joe bob = Joe Values After Changing One Instance: joe = Bobbi Jo bob = Bobbi JoIn Listing 22-2, I created two Employee instances, joe and bob. You can see in the output that the Name properties of both Employee instances each show their assigned values from when the objects were first created. After assigning joe to bob, the value of the Name properties of both instances are the same. This is what you might expect to see.
What might surprise you is the values that occur after assigning a value to the Employee instance variable named joe. If you look at the code closely, you'll notice that it doesn't change bob - only Joe. However, the results from the output show that the Name property in bob is the same as the Name property in joe. This demonstrates that after assigning joe to bob, both variables held references to the joe object. Only the reference was copied - not the object. This is why you see the results of printing Name in both joe and bob are the same because the change was on the object that they both refer to.
The following types are reference types:
- arrays
- class'
- delegates
- interfaces
Understanding Value Types
Value type variables, as their name (value) suggests, hold the object value. A value type variable holds its own copy of an object and when you perform assignment from one value type variable to another, both the left-hand-side and right-hand-side of the assignment hold two separate copies of that value. Listing 22-3 shows how value type assignment works.Listing 22-3. Value Type Assignment
using System; struct Height { private int m_inches; public int Inches { get { return m_inches; } set { m_inches = value; } } } class Program { static void Main() { Height joe = new Height(); joe.Inches = 71; Height bob = new Height(); bob.Inches = 59; Console.WriteLine("Original Height Values:"); Console.WriteLine("joe = " + joe.Inches); Console.WriteLine("bob = " + bob.Inches); // assign joe value to bob variable bob = joe; Console.WriteLine(); Console.WriteLine("Values After Value Assignment:"); Console.WriteLine("joe = " + joe.Inches); Console.WriteLine("bob = " + bob.Inches); joe.Inches = 65; Console.WriteLine(); Console.WriteLine("Values After Changing One Instance:"); Console.WriteLine("joe = " + joe.Inches); Console.WriteLine("bob = " + bob.Inches); Console.ReadKey(); } }Here's the output:
Original Height Values: joe = 71 bob = 59 Values After Value Assignment: joe = 71 bob = 71 Values After Changing One Instance: joe = 65 bob = 71In Listing 22-3, you can see that the Inches property of bob and joe are initially set to different values. After assigning joe to bob, a value copy occurs, where both of the variables have the same value, but are two separate copies. To demonstrate value assignment results, notice what happens after setting joe to 65; The output shows that bob did not change, which demonstrates that value types hold distinct copies of their objects.
The following types are value types:
- enum
- struct
No comments:
Post a Comment