首页 > 值类型的装箱和拆箱

值类型的装箱和拆箱

值类型是比引用类型更“轻型”的一种类型,因为它不需要作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用。

        static void Main()

{

System.Collections.ArrayList list = new System.Collections.ArrayList();

Point p;//分配一个Point,不在堆中分配。

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

{

p.x = p.y = i;//初始化值类型的成员

list.Add(p);//对值类型进行装箱,并将引用添加到ArrayList中。C#编译器会自动的生成对一个值类型的实例进行装箱所需的IL代码,C#编译器检测到是向一个需要引用类型的方法传递一个值类型,所以会自动生成代码对对象进行装箱。在运行时,当前存在于Point值类型实例p中的字段会复制到新分配的Point对象中。已装箱的Point对象(已经是引用类型)的地址会返回给Add方法。Point对象会一直存在于堆中,直到被垃圾回收。Point值类型的变量p可以重用,因为ArrayList根本不知道关于它的任何事情。
           Point p1 = (Point)list[0];//拆箱  ,   
}

}

List.Add()方法需要一个object参数,Add需要获取对托管堆上的一个对象的引用(或指针)来作为参数。在代码中传递的是p,是一个值类型,为了使代码正确工作,Point已经进行了一次装箱。

对一个值类型的实例进行装箱操作时发生在内部的事情:

1.在托管堆中分配内存。

2.值类型的字段复制到新分配的堆内存中。

3.返回对象的地址,现在这个地址是对一个对象的引用,值类型现在是一个引用类型。

拆箱发生的事:

1.获取已装箱的对象的各个字段的地址。

2.将字段包含的值从堆中复制到基于栈的值类型实例中。

拆箱的代价比装箱的低得多。拆箱就是获取一个指针的过程,该指针包含在一个对象中的原始值类型,事实上,指针指向的是已装箱实例中未装箱的部分,所以和装箱不同,拆箱不要求在内存中复制任何字节,往往会紧接着拆箱操作发生一次字段的复制操作。

 

装箱和拆箱会对应用程序的速度和内存消耗产生不利影响,所以应该注意编译器在什么时候生成代码来自动这些操作,并尝试手动编写代码,尽量避免自动生成代码的情况。

        static void Main()

{

Int32 i = 5;

object obj = i;//装箱

Int16 n = (Int16)obj;//拆箱失败,抛出InvalidCastException异常。如果引用指向的对象不是所期待的值类型的一个已装箱实例,就抛出此异常。

Int16 nn = (Int16)(Int32)obj;//先拆箱为正确的类型,在进行转型。

Int32 d = (Int32)obj;//

Console.ReadKey();

}

在对一个对象进行拆箱时,只能将其转型为原先未装箱时的值类型。 

        static void Main()

{

Int32 i = 5;

object obj = i;//装箱

i = 123;

Console.WriteLine(i + "," + (Int32)obj);//发生了多少次装箱?WriteLine()需要获取一个String对象,但是当前没有String对象,所以采取

//某种方式对这些数据进行合并,创建一个String对象。

//为了创建String对象编译器生成代码来调用String对象的静态方法Concat。

//String.Concat方法有几个重载版本,所有版本的执行操作都是一样的,不同的是参数数量。

//这里连接3个数据项创建一个字符串,所以编译器选择的是

//public static String Concat(object obj0,object obj1,object obj2) 这个版本

//为第一个参数传递的是i,i是一个未装箱的值类型,装箱。

//obj1是一个String对象的引用。obj3要求一次拆箱操作。然后装箱。

Console.WriteLine(i.ToString() + "," + obj);//这里没有装箱操作。i调用ToString方法,返回一个String,String对象已经是引用类型,所以能直接传递给Concat方法,不需要任何装箱操作

Console.ReadKey();

}  



看看FCL,会发现许多方法都针对不同的值类型参数进行重载。大多数方法进行重载的唯一目的就是减少常值类型的装箱操作次数。

未装箱的值类型比引用类型更“轻型”,归结于以下两个原因:

1.值类型不再托管堆上。

2.值类型没有堆上的每个对象都有的额外成员:一个类型对象指针和同步块索引。

因为值类型没有同步块索引,所以哈,不能使用System.Threading.monitor类型的各种方法,实现不了同步。

在C#中只用使用接口才能修改一个已装箱的值类型中的字段,详情见126页。



转载于:https://www.cnblogs.com/smailxiaobai/archive/2011/11/23/2260403.html

更多相关:

  • Python 与 ABC 的一个重要区别在于其类型系统。ABC 采用静态类型,编译器会检查程序中的变量类型是否保持一致,如果不一致,程序就无法运行。并且,ABC与当时大多数静态语言不同,采用的是类型推导(和 Haskell 一样),而不是类型声明(比如 C 语言)。而 Python 采用动态类型,所有类型检查都是在程序运行过程中,而不...

  • python是计算机二级考试的科目之一,并没有级别的划分。其考试目标是测试考生掌握Python语言知识的程度和对Python语言的编程能力、调试能力和综合应用能力,在当下的应用中是十分重要的。 什么是python Python语言是一种解释运行、面向对象、扩展性强的程序设计语言,是大学生学习计算机编程能力、理解计算机解决问题的方法的...

  • 正在学C,书上老说空指针,或者说void指针,对于我这样的生手来说,理解非常容易造成混淆,因为void这个单词的意思也是空,到底空指针的意思是指指向地址为空的类型呢,还是指void类型的指针呢 (1)空指针所对应的是指指向的对象为空的指针。            不经发问,什么叫指向为空呢?要理解这点,必须理解如下几点(有点啰嗦,但...

  • 一、reponseType 1、什么是reponseType XMLHttpRequest.reponseType属性是一个枚举类型的属性,返回响应数据的类型,他允许我们手动的设置 返回数据的类型。如果我们将它设置为一个空字符串,它将默认的使用"text"类型。 当将reponseType设置为一个特定的类型的时候需要确保服务...

  • SNMP中,数据类型并不多。这里我们就讨论这些数据类型,而不关心这些数据类型在实际中是如何编码的。INTEGER一个变量虽然定义为整型,但也有多种形式。有些整型变量没有范围限制,有些整型变量定义为特定的数值(例如,IP的转发标志就只有允许转发时的或者不允许转发时的这两种),有些整型变量定义一个特定的范围(例如,UDP和TCP的端口号...

  • 原文出处: 韩昊    1 2 3 4 5 6 7 8 9 10 作 者:韩 昊 知 乎:Heinrich 微 博:@花生油工人 知乎专栏:与时间无关的故事   谨以此文献给大连海事大学的吴楠老师,柳晓鸣老师,王新年老师以及张晶泊老师。   转载的同学请保留上面这句话,谢谢。如果还能保留文章来源就更感激不尽了。 我保证这篇文章...

  • 原文出处: 韩昊   我保证这篇文章和你以前看过的所有文章都不同,这是 2012 年还在果壳的时候写的,但是当时没有来得及写完就出国了……于是拖了两年,嗯,我是拖延症患者…… 这篇文章的核心思想就是: 要让读者在不看任何数学公式的情况下理解傅里叶分析。 傅里叶分析不仅仅是一个数学工具,更是一种可以彻底颠覆一个人以前世界观的思维...

  • 很多Linux高手都喜欢使用screen命令,screen命令可以使你轻松地使用一个终端控制其他终端。尽管screen本身是一个非常有用的工具,byobu作为screen的增强版本,比screen更加好用而且美观,并且提供有用的信息和快捷的热键。 想象一下这样一个场景:你通过Secure Shell(ssh)链接到一个服务器,并...

  • NarrowbandPrimary Synchronization Signal时域位置每1个SFN存在一个NPSSSFNSubframeSymbol长度每个SFN5最后11个symbol11个symbols频域位置NB-IOT下行带宽固定180kHz,一个PRB,12个子载波。...

  •  [h1]反斜杠只能够阻止一个字符  [h2]位于键盘的左上角,和~公用一个键。...