C#之装箱和拆箱

    在实际编码过程中,有时候会出现装箱和拆箱操作。下面就类分别认识一下:

需要注意的是,类型转换和这个是不同的。Convert方法并没有发生装箱和拆箱操作,而是类型转换,包括int.parse等等。

装箱,是把值类型拷贝了一个副本放在堆内存中。

拆箱,在引用类型直接找到值类型存储的位置(Person对象是引用类型,但其Age属性是值类型,也存储在堆内存中),实际上我们往往拆箱后会用一个值类型变量接收它。

例1:

1             int n = 10;
2             Console.WriteLine(n);
3             object o = n;//一次装箱
4             Console.WriteLine((int)o);
5             Console.WriteLine(o);//这里输出的是字符串"10",相当于Console.WriteLine(o.ToString());

    上面的代码中,第2句话并没有发生装箱,因为Console.WriteLine()方法有一个int类型重载;第3句话发生一次装箱;而第4句话(int)o发生一次拆箱,拆箱后的值被其values接收,如果没有Console.WriteLine()方法有一个int类型重载的话,会马上再次装箱,因为这个重载,没有发生装箱;最后1句话,输出的是字符串"10",相当于Console.WriteLine(o.ToString())。所以在判断一句话是否发生装箱和拆箱要结合构造函数来看。

例2:

1             int n = 10;
2 
3             IComparable com = n;
4 
5             int m = (int)com;
6 
7             Console.WriteLine(m);

     上面的代码中,第二句话发生了装箱,第三句话发生了拆箱,因为int类型实现了IComparable接口。

例3:

1             int d = 999;
2             object o = d; //装箱
3 
4             //装箱时使用什么类型,拆箱时也必须使用同样的类型。否则报异常
5             double d1 = (double)o; //拆箱 
6 Console.WriteLine(d1);

例3中,编译会报错,以为在第2句话装箱前是int类型,而拆箱时确是double。需要注意的是,装箱前是什么类型,拆箱后也必须是什么类型,否则会报错

例4:

 1            int n = 100;
 2             M1(n);
 3 
 4             string x = "a";
 5             double d1 = 10;
 6             string y = "b";
 7             int n1 = 9;
 8             string z = "c";
 9             string full = x + d1 + y + n1 + z;
10             Console.WriteLine(full);
11 
12        //定义函数如下:
13         static void M1(double d)
14         {
15             Console.WriteLine(d);
16         }
17 
18         static void M1(object o)
19         {
20             Console.WriteLine(o);
21         }

上面的代码到底发生了几次装箱和拆箱呢?首先看M1()方法,经反编译得知M1((double) n);由此可见,M1()方法并没有去找object类型的重载,而是找了与之相近的double类型重载,所以没有发生装箱。第9句话经反编译得知Console.WriteLine(string.Concat(new object[] { x, d1, y, n1, z }));它将double类型变量d1和int类型变量n1装箱成object类型。所以上面的代码只发生了2次装箱。

装箱和拆箱带来的性能影响:

日常编码中应避免装箱和拆箱,因为他们会带来性能的损耗。看下面的代码:

1             ArrayList aList = new ArrayList();
2             Stopwatch sw = new Stopwatch();
3             sw.Start();
4             for (int i = 0; i < 100000; i++)
5             {
6                 aList.Add(i);   //Add方法每添加一次发生一次装箱
7             }
8             sw.Stop();
9             Console.WriteLine("用时:"+sw.Elapsed);

运行结果:

我们换另一种:

1             List<int> aList = new List<int>();
2             Stopwatch sw = new Stopwatch();
3             sw.Start();
4             for (int i = 0; i < 100000; i++)
5             {
6                 aList.Add(i);   
7             }
8             sw.Stop();
9             Console.WriteLine("用时:" + sw.Elapsed);

运行结果:

由此可见,性能影响还是挺大的。

总结:

一)装箱和拆箱的判断依据:
1.发生在值类型和引用类型之间;
2.具备父子类的关系。

二)判断一句话是否发生装箱和拆箱要结合构造函数来看;

三)装箱前是什么类型,拆箱后也必须是什么类型,否则会报错;

四)写代码的过程中应尽量避免装箱和拆箱带来的性能消耗。

原文地址:https://www.cnblogs.com/chens2865/p/3858162.html