c#基础语言编程-装箱和拆箱
引言
为什么有装箱和拆箱,两者起到什么作用?NET的所有类型都是由基类System.Object继承过来的,包括最常用的基础类型:int, byte, short,bool等等,就是说所有的事物都是对象。如果程序中所有的类型操作用的是引用类型时,往往导致效率低下,所以.Net通过将数据类型分为值类型和引用类型。
前面文章中讲过;
- 值类型
定义:值类型是在栈中分配内存,在声明时初始化后才能使用,不能为null。
a、整型:(Sbyte、Byte、Char、Short、Ushort、Int、Uint、Long、Ulong)
b、浮点型:(Float、Double)、decima、bool
c、用户定义的结构(struct)。
- 引用类型
定义:引用类型是在托管堆中分配内存空间用于存储数据、数据指针、以及Sync等,初始化默认为null。
包括类、接口、委托、数组以及内置引用类型object与string。
存储类型
什么是堆,什么是栈?
1、堆区(heap) 一般由程序员进行申请、释放,若程序员不释放,在程序退出时内存自动释放。
2、栈区(statck)- 由编译器自动分配释放,存放函数的参数值,局部变量的值
3、全局区(静态区)-全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
装箱和拆箱定义和过程
通俗上讲,装箱是讲值类型转化为引用类型,
拆箱是将引用类型转化为值类型 。
将值类型与引用类型链接起来为何需要装箱?(为何要将值类型转为引用类型?)
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。
- 装箱操作(boxing):
1、首先从托管堆中为新生成的引用对象分配内存。
2、然后将值类型的数据拷贝到刚刚分配的内存中。
3、返回托管堆中新分配对象的地址。
可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
- 拆箱操作(unboxing):
1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。
2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
- 代码实例:
c#代码
bject objValue = 4;
int value = (int)objValue;
上面的两行代码会执行一次装箱操作将整形数字常量4装箱成引用类型object变量objValue;然后又执行一次拆箱操作,将存储到堆上的引用变量objValue存储到局部整形值类型变量value中。
Il代码
locals init (
[0] object objValue,
[1] int32 'value'
) //上面IL声明两个局部变量object类型的objValue和int32类型的value变量
IL_0000: nop
IL_0001: ldc.i4.4 //将整型数字4压入栈
IL_0002: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间
IL_0007: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中
IL_0008: ldloc.0//将索引为0的局部变量(即objValue变量)压入栈
IL_0009: unbox.any [mscorlib]System.Int32 //执行IL 拆箱指令unbox.any 将引用类型object转换成System.Int32类型
IL_000e: stloc.1 //将栈上的数据存储到索引为1的局部变量即value
上述代码中有几个box意味着有几次装箱操作,有几个unbox就是拆箱操作。
在拆箱中注意的问题:
int x = 0;
Int32 y = new Int32();
Object o ;
o = x; //隐式的装箱。
o = (Int32)y; //显示的装箱。
对于装箱而言,是不存在任何疑问点,既可以用显示(Explicit),也可以用隐式(Implicit)。
x = o; //Error;
x = (int)o 或者 x = (Int32)o; //Right;
拆箱必须是显示的,而不是隐式的。
Int32 x = 5;
Int64 y = 6;
object o;
o = x; or o = (Int32)x;
y = (Int64)o; //It's Error.
装箱的类型必须与拆箱的类型一致。而不是什么可隐式转换之类的。所以装箱的时候用的是Int32,拆箱的时候必须是Int32。
避免装箱的方法
通过泛型来避免。
- 非泛型集合
var array = new ArrayList();
array.Add(1);
array.Add(2);
foreach (int value in array)
{
Console.WriteLine(“value is {0}”,value);
}
在向ArrayList中添加int类型元素时会发生装箱,在使用foreach枚举ArrayList中的int类型元素时会发生拆箱操作,将object类型转换成int类型,在执行到Console.WriteLine时,还会执行两次的装箱操作;这一段代码执行了6次的装箱和拆箱操作;
- 泛型集合
var list = new List<int>();
list.Add(1);
list.Add(2);
foreach (int value in list)
{
Console.WriteLine("value is {0}", value);
}
代码和1中的代码的差别在于集合的类型使用了泛型的List,而非ArrayList;我们同样可以通过查看IL代码查看装箱拆箱的情况,上述代码只会在Console.WriteLine()方法时执行2次装箱操作,不需要拆箱操作。
通过重载函数来避免。
Struct A : ICloneable
{
public Int32 x;
public override String ToString() {
return String.Format(”{0}”,x);
}
public object Clone() {
return MemberwiseClone();
}
}
static void main()
{
A a;
a.x = 100;
Console.WriteLine(a.ToString());
Console.WriteLine(a.GetType());
A a2 = (A)a.Clone();
ICloneable c = a2;
Ojbect o = c.Clone();
}
5.0:a.ToString()。编译器发现A重写了ToString方法,会直接调用ToString的指令。因为A是值类型,编译器不会出现多态行为。因此,直接调用,不装箱。(注:ToString是A的基类System.ValueType的方法)
5.1:a.GetType(),GetType是继承于System.ValueType的方法,要调用它,需要一个方法表指针,于是a将被装箱,从而生成方法表指针,调用基类的System.ValueType。(补一句,所有的值类型都是继承于System.ValueType的)。
5.2:a.Clone(),因为A实现了Clone方法,所以无需装箱。
5.3:ICloneable转型:当a2为转为接口类型时,必须装箱,因为接口是一种引用类型。
5.4:c.Clone()。无需装箱,在托管堆中对上一步已装箱的对象进行调用。
有时我们可以提前进行装箱或者拆箱操作
比如:
//当我们如下时:
for (int i = 0; i < arrList.Length; i++)
{
//
}
//我们更因该这样:
int L = arrList.Length;
for (int i = 0; i < L; i++)
{
//
}
附:其实上面的基于一个根本的原理,因为未装箱的值类型没有方法表指针,所以,不能通过值类型来调用其上继承的虚方法。另外,接口类型是一个引用类型。对此,我的理解,该方法表指针类似C++的虚函数表指针,它是用来实现引用对象的多态机制的重要依据
凡事并不能绝对,假设你想改造的代码为第三方程序集,你无法更改,那你只能是装箱了。
c#基础语言编程-装箱和拆箱的更多相关文章
- 一、基础篇--1.1Java基础-包装类的装箱和拆箱
包装类:java是典型的面向对象编程,但是八种基本数据类型并不支持面向对象编程.基本类型的数据不具备对象的特性,没有属性和方法.沿用它们只是为了迎合人类根深蒂固的习惯,并的确能简单.有效地进行常规数据 ...
- Java基础 【自动装箱和拆箱、面试题】
JDK 1.5 (以后的版本)的新特性自动装箱和拆箱 1. 自动装箱:把基本类型转换为包装类类型 int a =10; Integer i = new Integer(a); Integer valu ...
- C#基础学习之装箱,拆箱
装箱,拆箱这两个的大条件是有继承关系. 装箱:值类型转换为引用类型 拆箱:引用类型转换为之类 但是要注意大条件. string (引用类型) int(值类型) 这个转换因为没有继承关系,内存中没 ...
- Java基础(十四)--装箱、拆箱详解
Java中基本数据类型都有相对应的包装类 什么是装箱?什么是拆箱? 在Java SE5之前,Integer是这样初始化的 Integer i = new Integer(10); 而在从Java SE ...
- java 基础知识七 装箱和拆箱
java 基础知识七 装箱和拆箱 数据类型可分为两大种,基本数据类型(值类型)和类类型(引用数据类型) 装箱:把基本类型用他们相对应的引用类型包装起来,使他们可以具有对象的特质 基本数据类型 ...
- Java 基础 - 装箱, 拆箱
总结 1-装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的.(xxx代表对应的基本数据类型).例如:在装箱的时候自动调用的是Integer的 ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
- 基础系列(4)—— C#装箱和拆箱
一 装箱和拆箱的概念 装箱是将值类型转换为引用类型 : 拆箱是将引用类型转换为值类型 : 值类型:包括原类型(Sbyte.Byte.Short.Ushort.Int.Uint.Long.Ulong.C ...
- 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...
随机推荐
- 日期-用Datapicker实现前一天后一天
运用了JQuery UI Datepicker 插件和一些常用日期的方法.其中Datepicker的API具体可参考[http://api.jqueryui.com/datepicker/#optio ...
- js获取星期几
function getweek(time) { /*显示星期*/ /*time为date格式*/ var str; var d = time.getDay(); switch (d) { case ...
- HIVE:用外连接替代子查询
由于hive也支持sql,很多人会把hql跟标准sql进行比较,甚至有的时候会直接套用.hive不支持事务也不支持索引,更不支持追加写,但是对于一般的sql都是能够支持的.但是对于一些子查询确实无法支 ...
- linux下删除内核
一.概述 笔者的Ubuntu系统刚安装成功后,就不知道怎么会有多个内核,但实际上默认运行的只有一个.在grub启动界面多余的启动项和多余内核占用的存储空间迫使我产生了铲除多余内核的冲动. 最近,自己从 ...
- wndows 7 Wifi热点
2016年09月13日 14時52分 wanglinqiang整理 Step1 cmd.exe(管理员身份运行) Step2 命令行输入[netsh wlan set hostednetwork mo ...
- struts文件上传拦截器中参数的配置(maximumSize,allowedTypes ,allowedExtensions)问题
<interceptor-ref name="fileUpload"> <param name="allowedTypes">image ...
- nutch-1.7 编译
转载自:http://peigang.iteye.com/blog/1563288 从nutch-.3开始 本地抓取(单击) 和 分布式抓取(集群)所使用的配置文件和命令单独分开. 资源:下载地址:h ...
- 判断是否为ie(包含ie11)
if (!!window.ActiveXObject || "ActiveXObject" in window) { alert("IsIE"); }
- BZOJ 3083 遥远的国度 树链剖分
3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 797 Solved: 181[Submit][Status] Descrip ...
- GCD介绍(一): 基本概念和Dispatch Queue
什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像NSOperationQueue,他们都允许 ...