基础系列(4)—— C#装箱和拆箱
一 装箱和拆箱的概念
装箱是将值类型转换为引用类型 ;
拆箱是将引用类型转换为值类型 ;
值类型:包括原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚举 (enum) 、结构 (struct)。
引用类型:包括类、数组、接口、委托、字符串等,我们利用代码说明一下:
int n = ;
object obj = n;
Console.WriteLine("装箱之前的数字为{0},装箱之后的数字为{1}",n,obj.ToString());//此处为装箱操作,将数值类型转换为object类型 int m = (int)obj;
Console.WriteLine("拆箱之前的数字为{0},拆箱之后的数字为{1}", obj.ToString(),m);//此处为拆箱操作,将object类型转换为数值类型
二 为什么需要装箱?
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。 另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。
三 拆箱和装箱的优缺点
装箱和拆箱虽然满足了两只类型之间的转换。但是从装箱的过程中不难看出,每次装箱时要在堆中new一个新的对象,当量特别大是肯定会大大影响程序的效率。所以,在应用中,我们应该尽量避免装箱操作。
我们用代码说明装箱与拆箱的效率:
ArrayList List = new ArrayList();//声明一个数组集合
//List<int> list = new List<int>(); //声明一个泛型集合
Stopwatch sw = new Stopwatch(); //用于测量运行时间 //00:00:01.4535918
//00:00:00.1996060 sw.Start();
for (int i = ; i < ; i++)
{
List.Add((object)i);//此处发生了装箱的操作
//list.Add(i);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
上面的代码是装箱的操作:

下面是普通的操作:
//ArrayList List = new ArrayList();//声明一个数组集合
List<int> list = new List<int>(); //声明一个泛型集合
Stopwatch sw = new Stopwatch(); //用于测量运行时间 //00:00:01.4535918
//00:00:00.1996060 sw.Start();
for (int i = ; i < ; i++)
{
//List.Add((object)i);//此处发生了装箱的操作
list.Add(i);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);

通过上面我们可以看到装箱与拆箱的时间对比,了解了装箱和拆箱的操作,我们可以清楚的明白:装箱操作会导致数据在堆和栈上进行拷贝,频繁的装箱操作会性能损失。而相比而言拆箱过程对性能损耗还是比较小的。
四 装箱与拆箱的过程
我们先看装箱时都会发生什么事情,下面是一行最简单的装箱代码
object obj = ;
这行语句将整型常量1赋给object类型的变量obj; 众所周知常量1是值类型,值类型是要放在栈上的,而object是引用类型,它需要放在堆上;要把值类型放在堆上就需要执行一次装箱操作。
这行语句的IL代码如下,请注意注释部分说明:
locals init (
[] object objValue
) //以上三行IL表示声明object类型的名称为objValue的局部变量
IL_0000: nop
IL_0001: ldc.i4.s //表示将整型数9放到栈顶
IL_0003: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间
IL_0008: stloc. //弹出堆栈上的变量,将它存储到索引为0的局部变量中
以上就是装箱所要执行的操作了,执行装箱操作时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这肯定是要消耗内存和cpu资源的。我们再看下拆箱操作是怎么回事:
请看下面的C#代码:
object objValue = ;
int value = (int)objValue;
上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。
同样我们需要看下IL代码:
.locals init (
[] object objValue,
[] int32 'value'
) //上面IL声明两个局部变量object类型的objValue和int32类型的value变量
IL_0000: nop
IL_0001: ldc.i4. //将整型数字4压入栈
IL_0002: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间
IL_0007: stloc. //弹出堆栈上的变量,将它存储到索引为0的局部变量中
IL_0008: ldloc.//将索引为0的局部变量(即objValue变量)压入栈
IL_0009: unbox.any [mscorlib]System.Int32 //执行IL 拆箱指令unbox.any 将引用类型object转换成System.Int32类型
IL_000e: stloc. //将栈上的数据存储到索引为1的局部变量即value
今天就先说这么多了,有些知识是参考网络上的,希望我们一起进步!
基础系列(4)—— C#装箱和拆箱的更多相关文章
- C#基础加强(5)之装箱与拆箱
定义 装箱:将值类型赋值给 Object 类型变量时,就是装箱操作,即包装为 Object 对象. 因为值类型都是 ValueType 类型,而 ValueType 页继承了 Object(CLR 内 ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
- 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...
- c#基础语言编程-装箱和拆箱
引言 为什么有装箱和拆箱,两者起到什么作用?NET的所有类型都是由基类System.Object继承过来的,包括最常用的基础类型:int, byte, short,bool等等,就是说所有的事物都是对 ...
- Java 基础 - 装箱, 拆箱
总结 1-装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的.(xxx代表对应的基本数据类型).例如:在装箱的时候自动调用的是Integer的 ...
- C#基础回顾(二)—页面值传递、重载与重写、类与结构体、装箱与拆箱
一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C ...
- Java基础(35):装箱与拆箱---Java 中基本类型和包装类之间的转换(Wrapper类)
基本类型和包装类之间经常需要互相转换,以 Integer 为例(其他几个包装类的操作雷同哦): 在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了. 那什么是装箱 ...
- Java基础 【自动装箱和拆箱、面试题】
JDK 1.5 (以后的版本)的新特性自动装箱和拆箱 1. 自动装箱:把基本类型转换为包装类类型 int a =10; Integer i = new Integer(a); Integer valu ...
- JAVA基础之——三大特征、接口和抽象类区别、重载和重写区别、==和equals区别、JAVA自动装箱和拆箱
1 java三大特征 1)封装:即class,把一类实体定义成类,该类有变量和方法. 2)继承:从已有的父类中派生出子类,子类实现父类的抽象方法. 3)多态:通过父类对象可以引用不同的子类,从而实现不 ...
随机推荐
- MySQL->元数据[20180510]
MySQL元数据 Meta Data,一般是结构化数据(如存储在数据库里的数据,字段长度.类型.默认值等等).Meta Data就是描述数据的数据,在MySQL中描述有哪些数据库.哪些表.表有 ...
- PHP-----PHP程序设计基础教程----第三章函数
3.1 初识函数 3.1.1 函数的定义 语法: function 函数名([参数1,参数2,......]) { 函数体 } (1)function:在声明函数时必须使用的关键字 (2)函数名:创建 ...
- Spring 约束文件配置
1.引入jar包 2.新建applicationContext.xml配置文件 位置随意,建议放在src目录下 新建的空xml文件,写入一对beans标签 3.打开Windows-->Prefe ...
- flask中的蓝图实现模块化的应用
Blueprint 蓝图的基本设想是当它们注册到应用上时,它们记录将会被执行的操作. 当分派请求和生成从一个端点到另一个的 URL 时,Flask 会关联蓝图中的视图函数. 简单来说,Blueprin ...
- redis必会
1.NosqL 非关系型数据库,里面包含Redis和MondoDB2.为什么会用到关系型数据库?因为当数据量太多,访问人数过多的时候,在访问关系型数据库时会到硬盘里进行读写过多 这样就会导致访问速度很 ...
- 【原创】Dynamics CRM 2015/2016,以PDF的形式打开SSRS报表。
基本步骤: 使用SSRS建立报表,以下的例子是以记录的GUID作为报表的参数 获取ReportSession 和 ControlId来调用报表 以PDF的形式预览报表 一.根据报表的名称获取报表的GU ...
- Python学习 :常用模块(三)----- 日志记录
常用模块(三) 七.logging模块 日志中包含的信息应有正常的程序访问日志,还可能有错误.警告等信息输出 python的 logging 模块提供了标准的日志接口,你可以通过它存储各种格式的日志, ...
- 磁砖样式——第八届蓝桥杯C语言B组(国赛)第二题
原创 标题:磁砖样式 小明家的一面装饰墙原来是 3*10 的小方格.现在手头有一批刚好能盖住2个小方格的长方形瓷砖.瓷砖只有两种颜色:黄色和橙色. 小明想知道,对于这么简陋的原料,可以贴出多少种不同的 ...
- dns欺骗之ettercap
ettercap是一个基于ARP地址欺骗方式的网络嗅探工具,主要适用于局域网. ettercap是一款现有流行的网络抓包软件,它利用计算机在局域网内进行通信的ARP协议的缺陷进行攻击,在目标与服务器之 ...
- DP_括号匹配序列问题
括号匹配问题 简单括号匹配问题是给出字符串,判断字符串中的括号是否匹配,此类问题核心解决方案就是利用栈的后进先出的特性,从左到右依次遍历字符串,遇左括号进栈,遇右括号将其与栈顶元素配对,若能配对,则栈 ...