C# Tutorial - Dissecting Our Eighth Application – Converting from Value to Reference and Vice VersaAs well as casting from one type to another, there are times when you need to change from a value type to a reference type, or the other way round. When we discussed Types in section 3, we looked at structures which are a value type. So using the sample we had previously, we will have a structure to represent a coordinate: public struct Coords
{
public int X, Y;
public Coords(int p1, int p2) { X = p1; Y = p2; }
}
Imagine now that we want to store a list of coordinates that can shrink and grow using one of the collection classes within the System.Collections namespace. The collection class ArrayList, allows you to add and remove items dynamically. If we look at the help for the Add method of the ArrayList class, we see that it takes an object as a parameter. public virtual int Add ( Object value ) The problem now occurs that Coords is a value type, and the method requires a reference type. This is solved through the use of Boxing and Unboxing. Boxing is the technique of changing a value type into a reference type, while Unboxing takes a reference type and converts it to a value type. So let us see Boxing in action: // Create a List ArrayList aList = new ArrayList(); // Create a new coordinate Coords c = new Coords( 0, 1 ); // Add the coordinate to the list aList.Add( c ); The code looks as though it adds the value type to the list, but what really happens is that when the Add method is called, memory is allocated in the heap for a Coords object. The members currently residing in the Coords value type (c) are copied into the newly allocated Coords object. The address of the Coords object is returned and is then passed to the Add method. When the Coords object is no longer needed, the memory will be freed up by the system. The Coords value type variable can be reused or removed at any time because the ArrayList does not know anything about it. We can then Unbox the objects from the ArrayList: // Create a coordinate from the Coords object at index 0 Coords d = (Coords)a[0]; Explicit BoxingThe previous example did the boxing for us so we did not have to write any code to do this. This is known as implicit boxing. Just like casting between types, we can explicitly box a value type into an object. int i = 123; object o = (object)i; // explicit boxing The diagram below shows what is happening in memory. ![]() Once the value type has been boxed, changing either the value type or the boxed reference type does not affect the contents of the other. We can show this with a simple piece of code: int i = 123; object o = (object)i; // explicit boxing i = 789; After this code has been executed, the value of i is 789 whilst the value of o is 123. Explicit UnboxingAll unboxing must be explicit, you need to specify what type the object is being unboxed to. However, you cannot unbox an object into a different type. Take the following code as an example: Int32 i = 123; Int64 j = 456; object o = (object)i; j = (Int64)o; This example will compile, but at runtime will generate an exception of type InvalidCastException. This is because i is boxed as an Int32, so therefore it must be unboxed as an Int32. To allow this code to work, a small change is required. j = (Int64)(Int32)o; Object o must first be unboxed to an Int32 and then cast to an Int64.
|