In the CLR, in order to convert a value type to a reference type, a mechanism called boxing is used.
The following summarizes what happens inside a boxing operation on one instance of a value type: 1) allocates memory in the managed heap. The amount of memory allocated is the amount of memory required for each field of a value type plus two additional members for all objects on the managed heap (
type Object PointerAnd
Synchronization Block Index) The amount of memory required. 2) The value type of the field is copied to the new allocated heap memory. 3) Returns the address of the object. Now, this address is a reference to an object, and the value type is now a reference type.
UnpackingThe boxing process is not reversed directly. The cost of unpacking is much lower than boxing. Unpacking is actually a process of getting a pointer to the original value type (data field) contained in an object. In fact, the pointer is pointing to the unboxed part of the boxed instance. Therefore, unlike boxing, unpacking does not require copying bytes in memory. One more point is that
after unpacking, the copy operation of the field is often followed by one time. When an instance of a boxed value type is unpacking, the following things happen inside. 1. If the variable containing the reference to the boxed value type instance is null, a NullReferenceException exception is thrown. 2. If the reference to the object is not a boxed instance of the expected value type, a InvalidCastException exception is thrown. The second one above means that the code will not work as you expect:
public static void Main () { Int32 x = 5; Object o = x; Int16 y = (Int16) o;//throws Exception}
When unpacking an object, you can only transform it to the original unboxed value type--int32, the following is the correct wording:
public static void Main () { Int32 x = 5; Object o = x; //Boxing x, o referencing the boxed object Int16 y = (Int16) (Int32) O; Unpacking first as the correct type, in boxing}
As mentioned earlier, once a unboxing is carried out, it is often followed by a copy of the field once. The following illustrates the unpacking and copying operations:
public static void Main () {point p = new Point ();//stack variable p.x = p.y = 1; Object o = P; The P is boxed, o refers to the boxed instance p = (point) o; Disassemble o, copy the field from the boxed instance to the stack variable}
On the last line, the C # compiler generates an IL instruction to unboxing o and generates another IL directive to copy these fields from the heap to the stack-based variable p.
Look at the code again:
public static void Main () {point p = new Point (); Stack variable p.x = p.y = 1; Object o = P; The P is boxed, o refers to the boxed instance //The X field of point becomes 2 p = (point) o; Disassemble O and copy the field from the boxed instance to the stack variable p.x = 2; Change the state of the variable o = p; Boxing p, o referencing the boxed instance}
The only purpose of the last three lines of code is to change the X field of a point from 1 to 2. To do this, first execute the unboxing once, perform a field copy at the same time, alter the field (on the stack), and then execute the boxing once (thereby creating a completely new boxed instance on the managed heap). Hopefully you'll appreciate the impact of boxing and unboxing/copy operations on application performance.
Take a look at a demo box and unboxing example:
private static void Main (string[] args) { Int32 v = 5; Create a disguise box for the value type variable Object o = v; o Refer to a boxed Int32 v = 123 with a value of 5 ; Modify the unboxed value to be 123 Console.WriteLine (v + "," + (Int32) o); Show "123,5"}
Can you see how many times the above code is boxed? If it is 3 times, will you mean it? Let's look at the generated IL code.
. method private Hidebysig static void Main (string[] args) CIL managed{ . entrypoint //code size . maxstack 3 . Locals init ( [0] int32 num, [1] object obj2) L_0000:nop //5 load into v l_0001:ldc.i4.5 l_0002: stloc.0 //V is boxed, the reference pointer is stored in O l_0003:ldloc.0 l_0004:box int32 l_0009:stloc.1 //123 is loaded into V L_000A:LDC.I4.S 0x7b l_000c:stloc.0 //V is boxed and the pointer remains on the stack for concat (connection) operation l_000d:ldloc.0 l_000e:box int32 // Loads a string onto the stack to perform a concat operation l_0013:ldstr "," //Unpacking o: Gets a pointer to the Int32 field on the stack l_0018:ldloc.1 l_0019: unbox.any int32 //boxing the Int32 and holding the pointer over the stack for concat (connection) Operation l_001e:box int32 //Call concat l_0023 : Call string [Mscorlib]system.string::concat (Object, Object, Object) //Pass the string back from Concat to WriteLine l_0028: call void [Mscorlib]system.console::writeline (string) L_002d:nop //return from main L_002e:ret}
Tip: The main reason is on the Console.WriteLine method.
The Console.WriteLine method requires a string object to be obtained, in order to create a string object, the C # compiler generates code to invoke the static method Concate of the string object. The method has several overloaded versions, the only difference being the number of parameters, which in this case requires the connection of three data items to create a string, so the compiler chooses the following concat method to invoke:
public static String Concat (objetc arg0, Object arg1, Onject arg2);
So, if you write a call to WriteLine like the following, the generated IL code will have a higher execution efficiency:
Console.WriteLine (v + "," + O); Show "123,5"
This is just the (Int32) cast before the variable o is removed. It avoids unpacking and boxing once.
We can also call WriteLine to further improve the performance of the above code:
Console.WriteLine (v.tostring () + "," + O); Show "123,5"
The ToString method is now called for the unboxed value type instance V, which returns a string. The string type is already a reference type, so it can be passed directly to the Concat method without any boxing operations.
The following shows a boxing and unboxing operation:
private static void Main (string[] args) { Int32 v = 5; Create a disguise box for the value type variable Object o = v; o Refer to a boxed Int32 v = 123 with a value of 5 ; Modify the unboxed value to be 123 Console.WriteLine (v) //display "123" v = (Int32) o; Remove the box and copy O to v Console.WriteLine (v); Show "5"}
How many times has the above code been boxed? The answer is once. Because the System.Console class defines an overloaded version of the WriteLine method that gets a Int32 as a parameter:
public static String Concat (Int32 value);
There may be boxing operations inside the WriteLine method, but this is not something we can control. We have eliminated the boxing operation from our own code as much as we can.
Boxing and unpacking of value types