1. 装箱转换
装箱转换是指将一个值类型隐式地转换成一个object 类型,或者把这个值类型转换成一个被该值类型应用的接口类型interface-type。把一个值类型的值装箱,也就是创建一个object 实例并将这个值复制给这个object。比如:
int i = 10;
object obj = i;
用下图所示,可以表示装箱的过程
装箱的过程
我们也可以用显式的方法来进行装箱操作:
int i = 10;
object obj =(object)i;
2、拆箱转换
和装箱转换正好相反,拆箱转换是指将一个对象类型显式地转换成一个值类型,或是将一个接口类型显式地转换成一个执行该接口的值类型。
拆箱的过程分为两步:首先,检查这个对象实例,看它是否为给定的值类型的装箱值。然后,把这个实例的值拷贝给值类型的变量。
利用如下例子,查看一个对象拆箱的过程 。
int i = 10;
object obj = i;
int j = (int)obj;
拆箱的过程用图来表示就是:
拆箱的过程
可以看出拆箱过程正好是装箱过程的逆过程。必须注意,装箱转换和拆箱转换必须遵循类型兼容原则。
尽量避免装箱
我们之所以研究装箱和拆箱,是因为装箱和拆箱会造成相当大的性能损耗(相比之下,装箱要比拆箱性能损耗大),性能问题主要体现在执行速度和字段复制上。因此我们在编写代码时要尽量避免装箱和拆箱,常用的手段为:
1. 使用重载方法。为了避免装箱,很多FCL中的方法都提供了很多重载的方法。比如我们之前讨论过的Console.WriteLine方法,提供了多达19个重载方法,目的就是为了减少值类型装箱的次数。比如看下面的这段代码:
Console.WriteLine(3);
刚开始你可能觉的3会装箱为string类型,但是实际上这条语句不会进行装箱操作,是因为Console.WriteLine方法有一个重载的方法,参数就是一个int的值。
public static void WriteLine(int value);
类似Console.WriteLine方法,还有System.IO.BinaryWriter的Write 方法,System.IO.TextWriter 的Write和WriteLine方法,System.Text.StringBuilder的Append和Insert方法等都提供了大量的重载方 法,以减少装箱次数。所以我们在实际的项目中,应该时刻注意装箱的情况,并且选用合适的重载方法避免装箱。
2. 使用泛型。因为装箱和拆箱的性能问题,所以在.NET 2.0中引用了泛型,他的主要目的就是避免值类型和引用类型之间的装箱和拆箱。我们常用的集合类都有泛型的版本,比如ArrayList对应着泛型的 List<T>,Hashtable对应着Dictionary<TKey, Tvalue>。
3. 如果在项目中一个值类型变量需要多次拆装箱,那么可以将这个变量提出来在前面显式装箱。比如下面这段代码:
int j = 3;
ArrayList a = new ArrayList();
for (int i = 0; i < 100; i++)
{
a.Add(j);
}
// 可以修改为:
int j = 3;
object ob = j;
ArrayList a = new ArrayList();
for (int i = 0; i < 100; i++)
{
a.Add(ob);
}
4. ToString。这点单独列出来是因为虽然小,但是很实用。虽然表面上看值类型调用ToString方法是要进行装箱的,因为ToString是从基类 继承的方法。但是ToString方法是一个虚方法,值类型一般都重写了这个方法,所以调用ToString方法不会装箱。之前说过String.Format方法容易造成装箱,避免的最佳方法就是在调用这个方法前将所有的值类型参数都调用一次ToString方法。