.NET中的装箱和拆箱

在.NET中,装箱和拆箱是一组针对值类型转换成引用类型,引用类型转换为值类型的术语。

我们都知道在.NET中内存是分为堆栈和托管堆的,堆栈存放的是值类型的变量,托管堆中存储的是引用类型的对象。而装箱和拆箱是在两种类型之间发生转换的时候发生的。譬如如下代码

int x = 1;

Object o =x;

int x = 1;执行这句代码的时候x的值是分配在堆栈当中的。而Object我们都知道他是引用类型,这时CLR会对x进行装箱操作,将堆栈中的数据放置在托管堆中,由一个名为”o”的引用指向该对象。这个过程称为装箱,而拆箱是执行相反的操作。如int y = (int)o;可以看一下下面的一段程序。 ``\` class Program

{

    static void Main(string[] args)

    {

        int x = 1;

        object o = x;

        int y = (int)o;

    }

} \`\`\\\`

这段程序首先声明了一个值类型的x变量,然后转换成引用类型o,最后强制转换成值类型y。用IL Tools查阅一下编译好的IL语言。注:在IL语言中装箱的指令为box,拆箱的指令为unbox。

我们可以看到在IL_0004(对应object o=x)中计算机执行了一次装箱操作。而在IL_000b(对应int y =(int)o)中又执行了一次拆箱操作。装箱和拆箱是隐式的,是不用人工操作的,但这个也需要注意一点,就是性能问题。频繁装箱对计算机的性能影响很大,来做一个实验来看下面的代码。

class Program

{

static void Main(string[] args)

{

int count = 9999999;

WhithBox(count);

Other(count);

Console.ReadKey();

}

static void WhithBox(int count)

{

List list = new List();

TimeControler.TimeControl.Start();

for (int i = 0; i < count; i++)

{

list.Add(i);

}

TimeControler.TimeControl.Stop();

Console.WriteLine(“装箱:” + TimeControler.TimeControl.GetMilliseconds());

}

static void Other(int count)

{

List list = new List();

TimeControler.TimeControl.Start();

for (int i = 0; i < count; i++)

{

list.Add(i);

}

TimeControler.TimeControl.Stop();

Console.WriteLine(“非装箱:” + TimeControler.TimeControl.GetMilliseconds());

}

}

用一个List泛型来装载值类型的对象,第一个泛型采用object类型,第二个泛型采用int类型,为了验证第一个泛型是做装箱操作的,第二个泛型是不做装箱操作的。我们看一下这两个方法的IL语言。

在List中装载int类型的对象的时候,注意IL_0019,在Add之前需要一个装箱操作。

在List中,在调用Add方法之前是没有装箱操作的。 [![][image-4]][4] 来看一下运行的效率对比。

差距还是挺大的5:1,至于为什么泛型集合也是引用对象却不会导致装箱操作,这个以后再研究。重要的是应该有意识的避免这种大量的装箱操作。