Nicholas-Chen

导航

C#回忆录三(装箱与拆箱)

回忆一下,在C#里面所有的类型分为两种,值类型,引用类型。值类型分配在线程堆栈上,其变量包含的不是地址,而是其所有字段的值。引用类型被分配在托管堆上,变量包含的是其地址。那么,现在考虑类型ArrayList,但你执行下面这段代码时会发生什么呢?
1 ArrayList m_arrayList = new ArrayList();
2 int i=0;
3 m_arrayList.Add(i);
因为 m_arrayList接收的是一个object类型,也就是一个引用类型在托管堆上的地址,而我们传进去的是一个值类型,其变量包含的不是地址,而是其所有字段的值。所以会在这里执行所谓的装箱操作。
第一步:在托管堆上分配内存。内存大小为值类型实例本身的大小加上其它额外的将该值类型实例视为真正引用对象所需的空间。方法表指针,和一个SyncBlockIndex。
第二步:把值类型字段值拷贝到托管堆上。
第三步:返回
托管堆上分配的内存的地址。
于是一个值类型就变成了一个引用类型。此时,托管堆上的对象和原来的对象就没有什么关系了。在考虑下面的代码。
1 int v = (int)m_arrayList[0];
m_arrayList[0]包含的是一个object,也就是一个引用类型。然后被强制转换成int型,它是一个值类型。于是在这里会发生所谓的拆箱操作。
第一步:如果该引用为Null,会抛出NullReferenceException异常。
第二步:如果只想的对象不是一个期望的值类型的已装箱对象,会抛出InvalidCastException
异常.
第三步:获取已装箱对象中属于值类型的那部分字段的地址,把托管堆上的值拷贝到线程堆栈上。
考虑下面的代码。
1 Int32 x =5;
2 Object z = x;
3 Int16 y = (Int16)z;

逻辑上讲他是对的,但是对一个对像执行拆箱操作时,只能转型为他未装箱时的类型。所以应该写为:Int16 y = (Int16)(Int32)z;

posted on 2006-12-11 19:41  nicholas_chen  阅读(105)  评论(0)    收藏  举报