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.具备父子类的关系。

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

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

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

你可能感兴趣的:(C#)