1、使用场景
装箱的使用场景:想把值类型当做引用类型来使用,于是把值类型转换为引用类型。
拆箱的使用场景:想把引用类型当做值类型来使用,于是把引用类型转化为值类型。
再进一步,为什么想把值类型当做引用类型来使用? 比如:方法定义时接受Object 类型的参数,而你想传递int 类型,此时需要把int装箱为Object;对于一些非泛型的容器,为了保证通用,元素类型定义为Object,此时向容器内添加int,double的时候,需要装箱。
为什么想把引用类型当做值类型来使用呢?对于值类型装箱后的引用类型,你想直接使用值类型的数据。
2、实现原理
装箱:
(1)在堆上分配一块内存,该内存就是一个实例
(2)将值类型的数据复制到刚创建的实例中
(3)返回该实例的引用
拆箱:
(1)取出实例中的数据
(2)赋值给新的变量
3、辨别是否进行了装箱拆箱
拆箱是显式的,而装箱可以显式也可以隐式,关键是判断有没有把值类型当做引用类型来使用。
View Code
1 class Program
2 {
3 static void Main()
4 {
5 int a = 123;
6
7 // 显示的装箱,也可以认为是向上转型,是安全的。
8 //int(Int32)继承ValueType,ValueType 继承Object
9 object obj = a;
10
11 // 隐式的装箱,Int32是struct(值类型)
12 // Int32本身没有GetType方法,而是继承了Object的GetType方法。
13 Type type = a.GetType();
14
15 // 没有装箱,Int32是struct(值类型),重写了Object的ToString方法,
16 // a.ToString() 调用Int32 上的方法
17 string s = a.ToString();
18
19 // 显式的拆箱
20 int b = (int)obj;
21 }
22 }
4、尽量避免装箱拆箱
为什么?因为装箱拆箱耗费时间和空间。
如何避免?
(1)对于方法定义接受的参数为object的情况,过载一个方法,接受的参数为值类型。
(2)对于非泛型的容器,采用泛型容器代替。
5、如何修改已装箱的值类型
举例来说:
View Code
1 class Program
2 {
3 static void Main()
4 {
5 MyStruct myStruct = new MyStruct();
6 myStruct.a = 100;
7
8 object obj = myStruct;
9 ((MyStruct)obj).Modify();
10 }
11 }
12
13 struct MyStruct
14 {
15 public int a;
16 public void Modify()
17 {
18 this.a = 200;
19 }
20 }
1、对于已经装箱的obj,如果要修改内容,需要调用MyStruct的方法,因此就要把obj转化为struct,也就是拆箱。
2、拆箱后,会在栈上生成新的实例,调用方法不会影响obj。
也就是说,拆箱会在栈上产生新的实例,对栈实例的修改不会影响到原来的obj。
解决办法:人为干涉,不让obj进行拆箱操作。
让MyStruct实现接口IModify,在接口IModify中声明Modify方法,将obj 转型为 接口,这时候并没有进行拆箱,修改会产生副作用。如下:
View Code
1 class Program
2 {
3 static void Main()
4 {
5 MyStruct myStruct = new MyStruct();
6 myStruct.a = 100;
7
8 object obj = myStruct;
9 ((IModify)obj).Modify();
10 }
11 }
12
13 struct MyStruct:IModify
14 {
15 public int a;
16 public void Modify()
17 {
18 this.a = 200;
19 }
20 }
21
22 public interface IModify
23 {
24 void Modify();
25 }