1. Packing and unboxing are an abstract concept
2. Boxing is to convert the value type to a reference type;
Unboxing is to convert the reference type to a value type
With boxing and unboxing, you can link value types with reference types by allowing any value of the value type to be converted to the value of the Object type.
For example:
int val = 100;
object obj = val;
("The value of the object = {0}", obj);
This is a boxing process, a process of converting a value type to a reference type
int val = 100;
object obj = val;
int num = (int) obj;
("num: {0}", num);
This is an unboxing process, which is a process of converting a value type to a reference type and then converting a reference type to a value type
Note: Only objects that have been loaded into the box can be unboxed
3. In .NET, data types are divided into value types and reference (not equivalent to pointers in C++). Correspondingly, memory allocation is divided into two ways: one is the stack and the other is the heap (note: it is the managed heap)
Value types are only allocated in the stack.
Reference type allocates memory with managed heap.
The managed heap corresponds to garbage collection.
4: What is packing/unboxing?
Boxing: Used to store value types in the garbage collection heap. Boxing is an implicit conversion of a value type to an object type or any interface type implemented to this value type.
Unboxing: An explicit conversion from object type to value type or from interface type to value type that implements that interface.
5: Why do I need to pack? (Why do you need to convert a value type to a reference type?)
One of the most common scenarios is to call a method with a parameter of type Object, which can support any type for general purpose. When you need to pass a value type (such as Int32) in, you need to box.
Another way to use is that a non-generic container, also defines the element type as an Object to ensure universality. Therefore, when adding value type data to the container, it is necessary to box.
6: Internal operation of packing/unboxing
Packing
Assign an object instance to the value type in the heap and copy the value to the new object. Follow three steps.
Newly allocated managed heap memory (size is the value type instance size plus a method table pointer and a SyncBlockIndex).
Copy the instance field of the value type to the newly allocated memory.
Returns the address of the newly allocated object in the managed heap. This address is a reference to the object.
Someone understands this way: if you box Int32, the returned address points to an Int32. I don't think it's impossible to understand this way, but this is indeed problematic. One reason is that it is not comprehensive, and the other reason is that it points to Int32 and does not say its essence (in the managed heap).
Unboxing
Check the object instance to make sure it is a boxed value of the given value type. Copy the value from the instance into the value type variable.
In a book, unboxing is just a pointer to the value type part in the reference object, while the copy of the content is the trigger of the assignment statement. I don't think this is important. The most important thing is to check the nature of the object instance. The types of unboxing and packing must match. In this regard, on the IL layer, there is no way to see the principle. My guess is that maybe a method like GetType is called to take out the types and match (because it needs to be strictly matched).
7: The impact of packing/unboxing on execution efficiency
Obviously, it can be seen from the principle that when packing, a brand new reference object is generated, which will cause time loss, which means that efficiency is reduced.
So how to do it?
First, try to avoid packing.
For example, both cases in Example 2 above can be avoided. In the first case, it can be avoided by overloading the function. In the second case, it can be avoided by generics.
Of course, everything is not absolute. Suppose the code you want to transform is a third-party assembly and you cannot change it, then you can only pack it.
For optimization of packing/unboxing code, since packing and unboxing are implicit in C#, the fundamental method is to analyze the code, and the most direct way to analyze is to understand the principle links and view the decompiled IL code.
For example: There may be extra boxing in the circulation body, and you can simply use the advance boxing method to optimize.
8: Further understanding of packing/unboxing
Packing/unboxing is not as simple as mentioned above
For example: when boxing, it becomes a reference object and there will be an additional method table pointer. What is the use of this?
We can explore further through examples.
For example:
Struct A : ICloneable
{
public Int32 x;
public override String ToString() {
return (”{0}”,x);
}
public object Clone() {
return MemberwiseClone();
}
}
static void main()
{
A a;
= 100;
(());
(());
A a2 = (A)();
ICloneable c = a2;
Ojbect o = ();
}
(). The compiler found that A rewrites the ToString method and will directly call the ToString instruction. Because A is a value type, the compiler will not have polymorphic behavior. Therefore, call directly and not box. (Note: ToString is the base class method of A)
(), GetType is an inherited method. To call it, a method table pointer is needed, so a will be boxed, thereby generating a method table pointer and calling the base class. (In addition, all value types are inherited from).
(), because A implements the Clone method, there is no need to box.
ICloneable Transformation: When a2 is converted to interface type, it must be boxed because the interface is a reference type.
(). No boxing is required, call the object that has been boxed in the managed heap.
Appendix: In fact, the above is based on a fundamental principle. Because unboxed value types do not have method table pointers, the virtual methods inherited on them cannot be called through the value types. In addition, the interface type is a reference type. In this regard, my understanding is that the method table pointer is similar to the virtual function table pointer of C++, which is an important basis for implementing the polymorphic mechanism of reference objects.
9: How to change a boxed object
For boxed objects, because they cannot be called directly, they must be unboxed first and then called the method. However, unboxing again will generate a new stack instance, and the boxed object cannot be modified. I feel a little dizzy, I feel like I'm talking about tongue twisters. Let's give an example: (Append the change method in the above example)
public void Change(Int32 x) {
= x;
}
Called:
A a = new A();
= 100;
Object o = a; //Box into o, below, want to change the value of o
((A)o).Change(200); //Has it been changed? Not changed
The reason why it was not changed is that when o is unboxing, the temporary stack instance A is generated, so the change is based on temporary A and is not changed to the boxing object.
(Appendix: In managed C++, it is allowed to directly retrieve the instance reference obtained in the first step of unboxing, and directly change it, but C# does not work.)
So what should I do?
Well, through the interface method, the same effect can be achieved.
Implementation is as follows:
interface IChange {
void Change(Int32 x);
}
struct A : IChange {
…
}
Called:
((IChange)o).Change(200);//Has it been changed? Change it
Why can it be changed now?
When transforming o to IChange, it will not be boxed again, and of course it will not be unboxed, because o is already a reference type, and because it is an IChange type, change can be called directly. Therefore, the fields in the boxed object are changed to achieve the desired effect.
10. To convert the value type to a reference type, boxing is required:
First allocate memory to the newly generated reference object from the managed heap
Then copy the value type data into the memory you just allocated
Returns the address of the newly allocated object in the managed heap
It can be seen that when boxing, you need to perform two operations that are comparatively affecting performance: allocating memory and copying data.
To convert the reference type to a value type, unboxing is required:
First, get the address of the field in the managed heap that belongs to the value type. This step is strictly unboxing.
Copy the value in the reference object to the value type instance located on the thread stack.
After these 2 steps, it can be considered that the same boxing is inverse operation. Unboxing in a strict sense does not affect performance, but the subsequent copying of data will affect performance the same as in boxing operations.
11、
All types of NET are inherited by the base class, including the most commonly used basic types: int, byte, short, bool, etc., which means that everything is an object.
If allocating memory in the heap (HEAP) when declaring these types, it will cause extremely low efficiency! (The reasons and the differences between heap and stack will be discussed separately in another article!)
How can .NET solve this problem? It is by dividing types into value and reference type,
Value types and reference types defined in C#
Value type: original type (Sbyte, Byte, Short, Ushort, Int, Uint, Long, Ulong, Char, Float, Double, Bool, Decimal), enum (enum), structure (struct)
Reference types: class, array, interface, delegate, string, etc.
The value type is to allocate memory on the stack and initialize it while declaring it to ensure that the data is not NULL;
The reference type allocates memory in the heap and is initialized to null. The reference type requires GARBAGE COLLECTION to recycle memory. The value type is not used. If it exceeds the scope of action, the system will automatically release it!
Let’s talk about the definition of packing and unboxing!
Boxing implicitly converts a value into a reference object. for example:
int i=0;
obj=i;
This process is packing! It's just boxing!
Unboxing is to convert a referenced object into any value type! for example:
int i=0;
obj=i;
int j=(int)obj;
The first two sentences of this process are to box i, and the second sentence is to unbox obj!