【c#】装箱与拆箱

  从内存执行的角度来看,值类型的内存分配在线程的堆栈上,而引用类型的内存分配在托管堆上。所以从值类型向引用类型的转换,势必牵涉到数据的拷贝与指针引用等操做。c#

  装箱操做,大体过程为:在托管堆中分配新对象的内存,将值类型的字段拷贝到该内存中,而后返回该对象的地址,这样就完成了从值类型到引用类型的转变;拆箱操做,则是获取已装箱对象中来自值类型部分字段的地址。装箱与拆箱并不是彻底对称的互逆操做,拆箱并不包含字段的拷贝。数据结构

  概念雷区:性能

  1. 装箱与拆箱不是彻底对等的互逆操做。从内存的角度上看,拆箱的性能开销远小于装箱,只是在实际的执行中,拆箱以后常伴随着字段的拷贝,以c#为例,编译器老是自动产生拆箱以后的字段拷贝。
  2. 只有被装箱过的对象才能被拆箱,非全部的引用类型。将非装箱而来的引用类型强制转换为值类型,会抛出InvalidCastException异常。

分拆

  值类型,提供了轻量型的数据结构,具备较少的内存开销,对系统性能有明显的做用。而缺点是:缺省方法表指针,由于没法在指望System.Object或其继承类的方法上调用值类型。spa

  装箱过程解析线程

  1. 内存分配:在托管堆中分配内存空间,内存大小为欲装箱值类型的大小加上其余额外的内存空间,主要包括方法表指针与SyncBlockIndex,此两个成员用于CLR管理引用类型对象。
  2. 实例拷贝:将值类型的字段拷贝到新分配的内存中去
  3. 地址返回

  拆箱过程解析指针

  1. 实例检查:首先检查是不是null,如果抛出NullReferenceException;若非,检查对象实例,确保是给定值类型的装箱值,而且保证拆箱后的类型为原来的同一类型,不然会抛出InvalidCastException
  2. 指针返回:返回已经装箱对象中属于原值类型部分字段的地址。而附加成员:方法表指针与SyncBlockIndex对该指针是不可见的。
  3. 字段拷贝:将托管堆中实例的字段拷贝到线程的堆栈中。

性能

  1. 在实际的项目中留意发生隐式装箱的可能,并提供相应的多个重载方法来避免装箱的发生。
  2. 装箱与拆箱常常是以隐式发生的,在系统中显式的实现装箱操做,是提升性能的较好选择
  3. 泛型能有效减小了装箱与拆箱的发生,大大提升了系统的性能与稳定。

应用

  1. ArrayList与Array
  2. Hashtable
  3. 枚举
    枚举类型为典型的.Net值类型,能够被装箱为System.Object,System.ValueType和System.Enum,以及System.Enum实现的三个接口类型System.IComparable,System.IConvertible,System.IFormattable
  4. 关注不经意的隐式转换
     1 public static void Main()
     2 {
     3     int i = 100;
     4     //装箱
     5     i.GetType();
     6     //未装箱
     7     i.ToString();
     8     //显式装箱
     9     object o = i;
    10     Hashtable ht = new Hashtable();
    11     ht.Add("One", o);
    12     ht.Add("Two", o);
    13 }

    GetType方法由System.Object类型提供,所以值类型调用时必须执行装箱操做;而ToString方法则由int类型覆盖,所以不会装箱。Hashtable的Add方法接受System.Object类型的参数,所以经过显式的类型转换来减小隐式的装箱操做。code

相关文章
相关标签/搜索