SoFunction
Updated on 2025-03-07

About C# Understanding Packing and Unboxing

1. Understand packing

Simply put, boxing is storing data of a value type in a variable of reference type.

Suppose you create a local variable of type int in a method, and you want to represent this value type as a reference type, then it means that you have boxed the value.As shown below:

static void SimpleBox() 
{ 
  int myInt = 25; 
 
  // Packing operation  object boxedInt = myInt; 
}

To be precise, the boxing process is the process of assigning a value type to an Object type variable. When you box a value, CoreCLR allocates a new object on the heap and copies the value of that value type to the object instance. What is returned to you is a reference to the newly allocated object in the managed heap.

2. Understand unboxing

In turn,Object The process of converting the value of a reference type variable back to the corresponding value type in the stack is called unboxing.

Syntactically, the unboxing operation looks like a normal conversion operation. However, its semantics are completely different.CoreCLR First verify whether the received data type is equivalent to the boxed type, and if so, it copies the value back to the local variable based on the stack storage.

For example, if the followingboxedInt The underlying type is indeedint, then the unboxing operation is completed:

static void SimpleBoxUnbox() 
{ 
  int myInt = 25; 
 
  // Packing operation  object boxedInt = myInt; 
 
  // Unboxing operation  int unboxedInt = (int)boxedInt; 
} 


Remember that unlike doing typical type conversions, you have to unbox it into an appropriate data type. If you try to unbox a piece of data into an incorrect data type, it will be thrownInvalidCastException abnormal. For safety reasons, if you can't guaranteeObject For the types behind the types, it is best to use try/catch logic to wrap the unboxing operation, although this will be a bit troublesome. Consider the following code, it will throw an error because you are trying to box theint typeUnboxing into onelong type

static void SimpleBoxUnbox() 
{ 
  int myInt = 25; 
 
  // Packing operation  object boxedInt = myInt; 
 
  // Unboxing to the wrong data type will trigger a runtime exception  try 
  { 
    long unboxedLong = (long)boxedInt; 
  } 
  catch (InvalidCastException ex) 
  { 
    (); 
  } 
} 

3. Generated IL code

When the C# compiler encounters the boxing/unboxing syntax, it generates IL code that contains the boxing/unboxing operation. If you use the compiled assembly, you will see the corresponding boxing and unboxing operationsbox andunbox instruction:

.method assembly hidebysig static 
    void  '<<Main>$>g__SimpleBoxUnbox|0_0'() cil managed 
{ 
  .maxstack  1 
  .locals init (int32 V_0, object V_1, int32 V_2) 
    IL_0000:  nop 
    IL_0001:  ldc.   25 
    IL_0003:  stloc.0 
    IL_0004:  ldloc.0 
    IL_0005:  box        []System.Int32 
    IL_000a:  stloc.1 
    IL_000b:  ldloc.1 
    IL_000c:    []System.Int32 
    IL_0011:  stloc.2 
    IL_0012:  ret 
  } // end of method '<Program>$'::'<<Main>$>g__SimpleBoxUnbox|0_0' 


At first glance, packing/unboxing seems to be a useless language feature, and academicity is more than practical. After all, you rarely need to store a local value type in a local Object variable. However, the fact is that the boxing/unboxing process is quite useful because it allows you to assume that everything can be handled as an Object type, and CoreCLR will automatically help you with memory-related details.

4. Practical application

Let's take the actual application of packing/unboxing. Let's take the ArrayList class of C# as an example and use it to save a batch of integer data stored on the stack. Related method members of the ArrayList classListed as follows:

public class ArrayList : IList, ICloneable 
{ 
  ... 
  public virtual int Add(object? value); 
  public virtual void Insert(int index, object? value); 
  public virtual void Remove(object? obj); 
  public virtual object? this[int index] { get; set; } 
} 


Please note that the above ArrayList methods are all about Object type data. ArrayList is designed for manipulating objects (representing any type) and objects are data allocated on the managed heap. Please consider the following code:

static void WorkWithArrayList() 
{ 
  // When passed to the object method, the value type will be automatically boxed  ArrayList myInts = new ArrayList(); 
  (10); 
} 


Although you pass digital data directly into a method that requires the Object parameter, the data allocated in the stack will be automatically binned when the runtime is used. If you want to use an indexer to retrieve a piece of data from an ArrayList, you must use a conversion operation to unbox the heap-allocated object into a stack-allocated integer, because the indexer of the ArrayList returns the Object type, not the int type.

static void WorkWithArrayList() 
{ 
  // When passed to a method that requires object parameters, the value type is automatically boxed  ArrayList myInts = new ArrayList(); 
  (10); 
 
  // Unboxing occurs when the object is converted back to stack-based data  int i = (int)myInts[0]; 
 
  // Because of the object parameter required by WriteLine(), it has been repacked again  ("Value of your int: {0}", i); 
} 


Calling() Previously, the int value allocated in the stack was boxed, so it can be passed into the parameter asObject type of method. fromArrayList Retrieved inObject When a type of data is converted, it is unboxed into an int type. Finally, when it is passed to() When the method is boxed again, because the parameters of this method areObject type.

5. Summary

From a programmer's perspective, packing and unboxing are very convenient, and we do not need to manually copy and transfer data of value types and reference types in memory.

But the stack/heap memory transfer behind packing and unboxing also brings performance issues. The following summarizes the steps required to pack and unbox a simple integer number:

Allocate a new object in the managed heap;

The data value in the stack is transferred to the objects in the managed heap;

When unboxing, the values ​​stored on the objects in the heap are transferred back to the stack;

Unused objects on the heap will eventually be recycled by GC.

Although packing and unboxing operations often do not have a significant performance impact, if a collection like ArrayList contains thousands of pieces of data and your program will frequently operate this data, the performance impact will still be obvious.

Therefore, when we are programming, we should try to avoid packing and unboxing operations. For example, for the above ArrayList example, if the collection element types are consistent, a generic collection type should be used, such as using List, LinkedList instead, etc.

This is the end of this article about C language understanding packing and unboxing. For more related C language understanding packing and unboxing, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!