C# 装箱 与 拆箱

1、使用场景

装箱的使用场景:想把值类型当做引用类型来使用,于是把值类型转换为引用类型。

拆箱的使用场景:想把引用类型当做值类型来使用,于是把引用类型转化为值类型。

再进一步,为什么想把值类型当做引用类型来使用? 比如:方法定义时接受Object 类型的参数,而你想传递int 类型,此时需要把int装箱为Object;对于一些非泛型的容器,为了保证通用,元素类型定义为Object,此时向容器内添加int,double的时候,需要装箱。

为什么想把引用类型当做值类型来使用呢?对于值类型装箱后的引用类型,你想直接使用值类型的数据。

2、实现原理

装箱:

(1)在堆上分配一块内存,该内存就是一个实例

(2)将值类型的数据复制到刚创建的实例中

(3)返回该实例的引用

拆箱:

(1)取出实例中的数据

(2)赋值给新的变量

3、辨别是否进行了装箱拆箱

拆箱是显式的,而装箱可以显式也可以隐式,关键是判断有没有把值类型当做引用类型来使用。 

C# 装箱 与 拆箱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、如何修改已装箱的值类型

举例来说:

C# 装箱 与 拆箱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 转型为 接口,这时候并没有进行拆箱,修改会产生副作用。如下:

C# 装箱 与 拆箱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 }

 

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