[C# Tips]再谈值类型的装箱和拆箱

Freesc Huang @ HUST All Rights Reserved
2008-2-11

Keywords
.NET Framework,C#,值类型,装箱,拆箱,CLR

正文
半年之前,我曾经写过一篇关于值类型装箱问题的短文(这里),现在看来,有些东西当时还是没有完全说开,这次特地拿了一个例子再来谈谈。理解这些问题,对于一个.NET程序员来说很基础,也很重要,对我们理解CLR和编写高效的程序都是很有帮助的。至于什么是值类型,什么是装箱拆箱(box&unbox),在此不做赘述,先来看看下面短短的几行代码:

Code

程序很短很简单,定义了一张火车票(Ticket)的结构,它只包括起点,终点和里程,它是值类型(结构派生自System.ValueType)。而我们主要关注的是围绕这张火车票的几个输出。

首先,创建了一个火车票的实例t(第38行), 初始化为北京到汉口,1225公里,接着第一次调用Console.WriteLine,因为Console.WriteLine() 没有参数为Ticket的重载,这里会对t进行装箱(就像这里提到的一样),而这个“已装箱”的票(姑且称作tII)会被CLR默认为是个Object,然后在这个Object实例tII上调用ToString(),而此时CLR在这个已装箱的Ticket的方法表中发现这个类型重写了ToString()方法,它会隐式的调用这个方法让我们顺利的得到显示"From 北京 To 汉口,1225 km"。也许我注释掉的那句代码会让您更好的理解这个过程。

随后,还是在那个值类型的t(而非tII)上调用Rebook,改成去上海,1400公里。然后再调用WriteLine,经过与前面相同的过程,我们如愿得到了输出"From 北京 To 上海,1400 km"。

接着,我们显式地将t装箱,其实这两行代码(第48,49行)跟第43行注释掉的代码是一样的,WriteLine()方法本身就有一个参数为Object的重载。于是我们仍能如愿得到"From 北京 To 上海,1400 km"。

接下来这一句比较有趣了,我们将o指向的tII拆箱,将相应的字段复制到堆栈上一个值类型实例tIII中,然后再对tIII调用Rebook方法,将其改为从北京到广州,注意,这里的修改是在堆栈上直接进行的。与托管堆上的o没有任何关系,于是此时调用ConsoleWriteLine(o);输出仍然为"From 北京 To 上海,1400 km"。

为了不让tIII成为游离于我们控制之外的垃圾,我们让Rebook立刻返回给我们这个tIII,并复制给一个叫t2的实例,并输出,这样我们才得到了修改过的最终结果"From 北京 To 广州,2000 km"。
程序输出如下:


程序代码在这里
本文算作是对之前那篇随笔的补充和扩展,也希望通过这个例子能让大家更加明白值类型装箱拆箱的原理和CLR在这背后的 行为,达人们轻点拍。欢迎大家多交流:-)


 

posted on 2008-02-11 20:51  J.D Huang  阅读(854)  评论(2编辑  收藏  举报