Home  >  Article  >  Backend Development  >  C# basic knowledge compilation Basic knowledge (18) Boxing and unboxing of value types (1)

C# basic knowledge compilation Basic knowledge (18) Boxing and unboxing of value types (1)

黄舟
黄舟Original
2017-02-11 13:49:431268browse

It is actually very interesting to learn more about boxing and unboxing. First, let’s look at why boxing and unboxing occur?
Look at the following code:

    class Program
    {
        static void Main(string[] args)
        {
            ArrayList array = new ArrayList();

            Point p;//分配一个

            for (int i = 0; i < 5; i++)
            {
                p.x = i;//初始化值

                p.y = i;

                array.Add(p);//装箱
            }
        }
    }

    public struct Point
    {
        public Int32 x;

        public Int32 y;
    }

Loop 5 times, initialize a Point value type field each time, and then put it into the ArrayList. Struct is a value type structure, so what is stored in ArrayList? Let's take another look at the Add method of ArrayList. You can see the Add method in MSDN:
public virtual int Add(Object value),
You can see that the parameter of Add is of type Object, that is, the parameter it requires is a reference to an object. That is to say, the parameters here must be reference types. As for what a reference type is, there is no need to elaborate. It is nothing more than a reference to an object on the heap. However, for the convenience of understanding, let’s talk about heap and stack again.
1. Stack area (stack) - automatically allocated and released by the compiler, storing function parameter values, local variable values, etc.
2. Heap area (heap) - allocated and released by the programmer. If the programmer does not release it, it may be recycled by the OS when the program ends.
For example:

    class Program
    {
        static void Main(string[] args)
        {
            Int32 n;//这是值类型,存放在栈中,Int32初始值为0
            
            A a;//此时在栈中开辟了空间

            a = new A();//真正实例化后的一个对象则保存在堆中。
        }
    }

    public class A
    {
        public A() { }
    }

Going back to the above question, the Add method requires reference type parameters, what should I do? Then you need to use boxing. The so-called boxing is to convert a value type into a reference type. The conversion process is as follows:
1. Allocate memory in the managed heap. The amount of memory allocated is the amount of memory required by the individual fields of the value type plus the amount of memory required by two additional members (the type object pointer and the synchronized block index) that all objects in the managed heap have.
2. Copy the value type fields to the newly allocated memory.
3. Return the address of the object. At this point, the address is a reference to an object, and the value type has now been converted to a reference type.
In this way, in the Add method, a reference to a boxed Point object is saved. The boxed object will remain in the heap until the programmer handles it or the system garbage collects it. At this point, the lifetime of the boxed value type exceeds the lifetime of the unboxed value type.
With the above boxing, it is natural to unbox it. If you want to take out the 0th element of the array:
Point p = (Point)array[0];
What you need to do here is to get The reference of element 0 of ArrayList is put into Point value type p. In order to achieve this goal, how to implement it? First, obtain the address of each Point field of the boxed Point object. That’s it for unboxing. The values ​​contained in these fields are then copied from the heap into the stack-based value type instance. Unboxing is essentially the process of obtaining a reference to a primitive value type contained in an object. In fact, the reference points to the unboxed part of the boxed instance. Therefore, unlike boxing, unboxing does not require copying any bytes in memory. But there is another point, a field copy operation occurs immediately after unboxing.
Therefore, boxing and unboxing will have an adverse impact on the speed and memory consumption of the program, so pay attention to when the program will automatically perform boxing/unboxing operations, and try to avoid these situations when writing code.
When unboxing, pay attention to the following exceptions:
1. If the variable containing "a reference to the boxed value type instance" is null, a NullReferenceException will be thrown.
2. If the object pointed to by the reference is not a boxed instance of the expected value type, an InvalidCastException will be thrown.
For example, the following code snippet:

             Int32 x = 5;

            Object o = x;

            Int16 r = (Int16)o;//抛出InvalidCastException异常

Because it can only be converted to the original unboxed value type when unboxing. Modify the above code to:

             Int32 x = 5;

            Object o = x;

            //Int16 r = (Int16)o;//抛出InvalidCastException异常

            Int16 r = (Int16)(Int32)o;

This is correct.
After unboxing, a field copy will occur, as shown in the following code:

            //会发生字段复制
            Point p1;

            p1.x = 1;

            p1.y = 2;

            Object o = p1;//装箱,发生复制

            p1 = (Point)o;//拆箱,并将字段从已装箱的实例复制到栈中

Look at the following code segment again:

            //要改变已装箱的值

            Point p2;

            p2.x = 10;

            p2.y = 20;

            Object o = p2;//装箱

            p2 = (Point)o;//拆箱

            p2.x = 40;//改变栈中变量的值

            o = p2;//再一次装箱,o引用新的已装箱实例

The purpose here is to copy the x of p2 after boxing The value is changed to 40. In this way, you need to unbox once, copy the field to the stack once, change the value of the field in the stack, and then perform boxing once. At this time, you need to create a new boxed box on the heap. Example. From this we also see the impact of boxing/unboxing and copying on program performance.
Let’s look at a few more boxing and unboxing code snippets:

            //装箱拆箱演示
            Int32 v = 5;

            Object o = v;

            v = 123;

            Console.WriteLine(v + "," + (Int32)o);

Three boxings occurred here, and it can be clearly seen that

            Object o = v;

            v = 123;

But in Console.WriteLine Packing also happened once, why? Because there are string type parameters in WriteLine here, and everyone knows that string is a reference type, so (Int32)o needs to be boxed here. Here again, the problem of using the + sign to connect strings in the program is explained. If there are several value types during the connection, several boxing operations will be performed.
However, the above code can be modified:

            //修改后
            Console.WriteLine(v.ToString() + "," + o);

This way there is no boxing.
Look at the following code again:

 Int32 v = 5;

            Object o = v;

            v = 123;

            Console.WriteLine(v);

            v = (Int32)o;

            Console.WriteLine(v);

Only one boxing occurs here, that is, Object o = v here, and Console.WriteLine does not happen here because it overloads int, bool, double, etc. Packing.

The above is the content of the basic knowledge of C# Basic knowledge (18) Boxing and unboxing of value types (1). For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn