避免不必要的装箱与拆箱--降低意外的性能损失

先介绍一下什么装箱和拆箱。所谓装箱是指将值类型转换成引用类型的过程,而将引用类型转换为值类型就是拆箱。

装箱的例子:
                      int aa=3;//值类型
                      object fx=aa;//aa装箱成了fx
这里aaa是一个值类型,fx是一个引用类型,在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接

或间接从 Object 继承的。可以将任何类型的值赋给 object 类型的变量。

接着我们看一下编译器在这个过程中都做了些什么?用ILDASM工具查看下底层代码:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       11 (0xb)
  .maxstack  1
  .locals init ([0] int32 aaa,[1] object fx)
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  box        [mscorlib]System.Int32
  IL_0009:  stloc.1
  IL_000a:  ret
} // end of method Program::Main

.locals这里定义了两个局部变量,nop表示用于在填充字节代码时填充空间,ldc.i4.3表示压入指定的int32值,其值是3。stloc.0表示将值弹

出到第一个局部变量中,0代表第一个变量。ldloc.0表示将值压入到第一个局部变量里。

接下来执行box(装箱)将值类型转换为引用类型。然后stloc.1将值再弹入第二变量fx里。

拆箱的例子:
延续上面的例子,现在把装箱后的fa再转换成值类型:
                    int uu=(int)fx;//拆箱
可以看到,在装箱时并没有使用显示转换,但拆箱时却要使用。这是因为在拆箱时对象可能会被转换成任何类型,所以要明确的指出要转换的

类型,以便编译器能够检验出这种类型转换是否有效。

接下来再看一个例子:
   int i=9;
   object o=i;
   Console.writeLine(i +","+(int32)o);
大家看看,在上面这个过程中到底发生了几次装箱和拆箱的操作?答案是共发生了3个装箱和1个拆箱操作。
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       51 (0x33)
  .maxstack  3
  .locals init ([0] int32 i,
           [1] object o)
  IL_0000:  nop
  IL_0001:  ldc.i4.s   9
  IL_0003:  stloc.0
  IL_0004:  ldloc.0
  IL_0005:  box        [mscorlib]System.Int32
  IL_000a:  stloc.1
  IL_000b:  ldloc.0
  IL_000c:  box        [mscorlib]System.Int32
  IL_0011:  ldstr      ","
  IL_0016:  ldloc.1
  IL_0017:  unbox.any  [mscorlib]System.Int32
  IL_001c:  box        [mscorlib]System.Int32
  IL_0021:  call       string [mscorlib]System.String::Concat(object,
                                                              object,
                                                              object)
  IL_0026:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002b:  nop
  IL_002c:  call       string [mscorlib]System.Console::ReadLine()
  IL_0031:  pop
  IL_0032:  ret
} // end of method Program::Main
通过观察上面的代码,可以看到有3次装箱操作,而第3次装箱操作正是发生在String.Concat方法之前,可以看到这里的string.Concat使用了

三个object参数的重载,所以在Console.writeline(i+","+(int32)O)时,又对O进行了一次隐式装箱。

通过这里例子主要想说明由于.net具有类型自动处理能力,如果代码中隐式的装箱和拆箱操作过多会极大的降低性能,所以在构造代码时应该尽量避免这些无谓的性能损失。

posted on 2006-07-11 17:03  Sinodon  阅读(615)  评论(0)    收藏  举报

导航