值类型与引用类型
只所以要提这两个概念,是因为很好得理解这两个概念有助于我们写出比较高效的C#代码。
我们知道,C#中的每一种类型要么是值类型,要么是引用类型。所以每个对象要么是值类型的实例,要么是引用类型的实例。
引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。
作为所有类型的基类,System.Object提供了一组方法,这些方法在所有类型中都能找到,其中包含toString方法及clone等方法。
System.ValueType直接继承System.Object,即System.ValueType本身是一个类类型,而不是值类型;System.ValueType没有添加任何成员,但覆盖了所继承的一些方法,使其更适合于值类型。例如,ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。
简单了解了值类型与引用类型那么我们下面来看下C#中的装箱和拆箱的概念。
装箱和拆箱
装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作。
1. 装箱在值类型向引用类型转换时发生
2. 拆箱在引用类型向值类型转换时发生
1 object objValue = 4;
2
3 int value = (int)objValue;
上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。
同样我们需要看下IL代码:
1 .locals init (
2
3 [0] object objValue,
4
5 [1] int32 'value'
6
7 ) //上面IL声明两个局部变量object类型的objValue和int32类型的value变量
8
9 IL_0000: nop
10
11 IL_0001: ldc.i4.4 //将整型数字4压入栈
12
13 IL_0002: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间
14
15 IL_0007: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中
16
17 IL_0008: ldloc.0//将索引为0的局部变量(即objValue变量)压入栈
18
19 IL_0009: unbox.any [mscorlib]System.Int32 //执行IL 拆箱指令unbox.any 将引用类型object转换成System.Int32类型
20
21 IL_000e: stloc.1 //将栈上的数据存储到索引为1的局部变量即value
拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并给值类型变量。