局部变量值类型直接包含了它们的数据,而接口和Object包含的是它们的引用,当从一个值类型转换为它所实现的接口或基类object时,即装箱
装箱(从值类型转换到引用类型)步骤:
1.首先在堆中分配好内存。用于存放值类型的数据以及少许额外开销(包括方法表指针、SyncBlock-Index)
2.之后进行内存复制,栈上的值类型数据复制到堆上分配好的位置
3.更新对象或接口的引用,指向堆的位置
拆箱则与之相反。CIL指令unbox只对堆上的数据进行解引用,不包括从堆复制到栈的动作。但C#中多数会紧接在拆箱后发生一次复制动作。
下面介绍通过CIL查看,在一个特定代码片段中统计box/unbox指令的数量
C#代码 CIL代码
static void Main(string[] args) .method private hidebysig
static void Main() cil managed
{ { .entrypoint
//code size 21(0*15)
.maxstack 1
int number; .locals init ( [0] int 32 number,
object thing; [1] object thing)
number = 42; IL_0000:nop
IL_0001:ldc.i4.s 42
IL_0003:stloc.0
//Boxing IL_0004:ldloc.0
thing = number; IL_0005:box [mscorlib]System.Int32
IL_000a:stloc.1
//Unboxing IL_000b:ldloc.1
number = (int)thing; IL_000c:unbox.any [mscorlib]System.Int32
IL_0011:stloc.0
IL_0012:br.s IL_0014
return; IL_0014:ret
} } // end of method Program::Main
装箱进行不频繁,性能问题不大。但装箱易忽略!!!