【C#】 装箱 (boxing) 和拆箱 (unboxing)
目录:
1. 装箱和拆箱
2. 深入理解装箱和拆箱
3. int[] to object[],值类型数组到对象数组的转化
4. 使用泛型减少装箱和拆箱
1. 装箱和拆箱
装箱 就是把“值类型”转换成“引用类型”;
拆箱 就是把“引用类型”转换成“值类型”;
首先,我们要弄明白为什么需要装箱和拆箱。C#的所有类型,包括int、boo等,都继承自System.Object,但是却又有值类型和引用类型之分。这时你要问,int是继承自object类型的,object是引用类型,那为何int不是引用类型而是值类型的呢?这就涉及到装箱和拆箱的概念了。
我们知道对象是创建在堆上的,它的创建和销毁必然带来额外的CPU和内存消耗。如果将int,boo等微小而常用的数据类型都放在堆上创建和销毁,语言的性能将会被极大的限制,有时甚至是无法忍受的。C#将值类型和引用类型分开,值类型直接在栈中被创建,超过作用域后直接销毁。当需要值类型成为对象时,使用装箱操作,让值类型变为一个引用类型的对象。这样,我们就可以使用object作为通用的接口统一语言内的一切类型。
拆箱 在MSDN官方文档里用的是 取消装箱。事实上拆箱是装箱的逆操作,也就是说我们只对装过箱的引用类型(通常是object对象)进行拆箱操作。单纯拆箱操作的后果无法设想的。
装箱和拆箱是C#的核心概念,C#利用其完成类型系统的统一。有了装箱,任何类型的值都可以视为一个对象。CLR在装箱时是将值类型包装到System.Object的内部,再将其存储到托管堆上。拆箱是从对象中提取值类型。装箱是隐式的而拆箱是显示的。
//装箱 boxing
int i = 3 ; //分配在栈上
object o = i ;//隐式装箱操作,int i 在堆上
object b = (object)i ; //显示装箱操作
//拆箱 unboxing
int j = (int) o ;//显示拆箱(将对象o拆箱为int类型) int k = b ;//error!!, 不能隐式拆箱
拆箱 的操作包括
1,检查对象实例,以却确保它是给定值类型的装箱值。
2,将该值从实例复制到值类型变量中。
下面来看看这个例子:
int i=0;
System.Object obj=i;
Console.WriteLine(i+","+(int)obj);
其中共发生了3次装箱和一次拆箱!^_^,看出来了吧?!
第一次是将i装箱,第2次是输出的时候将i转换成string类型,而string类型为引用类型,即又是装箱,第三次装箱就是(int)obj的转换成string类型,装箱!
拆箱就是(int)obj,将obj拆箱!!
2. 深入理解装箱和拆箱
object o = 1 ;
这句话的IL代码如下:
.locals init ( [0] object objValue ) //以上三行IL表示声明object类型的名称为objValue的局部变量 IL_0000: nop IL_0001: ldc.i4.s 1 //表示将整型数1放到栈顶 IL_0003: box [mscorlib]System.Int32 //执行IL box指令,在内存堆中申请System.Int32类型需要的堆空间 IL_0008: stloc.0 //弹出堆栈上的变量,将它存储到索引为0的局部变量中
注意注释的部分。执行装箱操作时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这肯定是要消耗内存和cpu资源的。
object objValue = 4; int value = (int)objValue;
同样,看看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
拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并给值类型变量。装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型来减少装箱操作和拆箱操作消耗。
3. int[] to object[],值类型数组到对象数组的转化
我们不能直接把值类型的数组赋值给对象数组,例如:
int[] array = new int[] { 0 } ;
object[] oiArray = (object[])array;//error!! 不能将int[] 转换到 object[]
string[] a={"1","2","3"};
object[] osArray = a ;//正确,a是引用类型数组,不存在装箱和拆箱
(object[])a无法将a所有的值类型对象“直接”转换为引用类型,所以编译器不会通过这个转换。可以使用如下的方式达到目的:
int[] array = new int[] { 0 } ;
object[] oArray = new object[array.Length];
for(int i =0 ; i< array.Length ; i++)
{
oArray[i] = array[i]; //隐式装箱
}
4. 使用泛型减少装箱和拆箱
有时说使用泛型能提高C#程序的性能,有一部分性能的提升是由减少了装箱和拆箱带来的。考察下面的代码:
public class Test
{
object _o ; public Test(object o)
{
_o = o ;
}
} public class Test<T>
{
T _o ;
public Test(T o)
{
_o = o ;
}
}
第一个Test类中没有使用泛型,如果将一个int类型的值传入Test,将会引发多次的装箱和拆箱操作。而泛型类在实例化时已经明确了类型,复制操作时就不会有装箱和拆箱操作了。
引用:
1. 玉开 http://www.cnblogs.com/yukaizhao/archive/2011/10/18/csharp_box_unbox_1.html
2. MSDN https://msdn.microsoft.com/zh-cn/library/yz2be5wk.aspx
【C#】 装箱 (boxing) 和拆箱 (unboxing)的更多相关文章
- C#中的装箱(inboxing)和拆箱(unboxing)(简单理解)
装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作. 装箱:将一个值类型隐式地转换成一个object类型,或把这个值类型转换成一个被该值类型应用的接口类型,把一个值类型的值装箱,就是创建一个ob ...
- 你真的了解装箱(Boxing)和拆箱(Unboxing)吗?
所谓装箱就是装箱是将值类型转换为 object 类型或由此值类型实现的任一接口类型的过程.而拆箱就是反过来了.很多人可能都知道这一点,但是是否真的就很了解boxing和unboxing了呢?可以看下下 ...
- Java语法糖2:自动装箱和自动拆箱
前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...
- java 自动装箱、自动拆箱
/** * @描述:自动装箱,自动拆箱 * @date 2017年1月10日下午1:30:01 * 结论:包装类的"=="运算在不遇到算术运算的情况下不会自动拆箱, * 以及他们的 ...
- java基础40 可变参数、自动装箱和自动拆箱
一.可变参数 可变参数是jdk1.5新特性 1.1.可变参数的格式 数据类型...变量名 // 数据类型...变量名public static void sum(int...arr){ } 1.2.可 ...
- JavaSE的包装类,自动装箱和自动拆箱 ,字符窜转换,toString(),equals(), hashCode()的区别
一.基本数据类型和包装类 包装类均位于Java.lang包,包装类和基本数据类型的对应关系如下表所示: Primitive-Type Wrapper-Class byte ...
- Java连载77-Integer常用方法、Integer、int、String三者相互转化、自动装箱、自动拆箱
一.关于Integer中常用的方法 package com.bjpowernode.java_learning; public class D77_1_ { public static void ...
- jdk1.5新特性之-----自动装箱与自动拆箱
import java.util.ArrayList; /* jdk1.5新特性之-----自动装箱与自动拆箱. java是面向对象 的语言,任何事物都可以使用类进行描述,sun就使用了 一些类描述j ...
- 自动装箱与自动拆箱——JavaSE基础
自动装箱与自动拆箱 自动装箱与拆箱就是编译器蜜糖(Compiler Sugar) Integer a = 234; // 自动装箱,实际上是Integer a = Integer.valueOF(23 ...
随机推荐
- mysql的一些心得
1.unsigned修饰整型 ,既为非负数,用此类型可以增加数据长度! 类型 大小 范围(有符号) ...
- Maven中央存储库
当你建立一个 Maven 的项目,Maven 会检查你的 pom.xml 文件,以确定哪些依赖下载.首先,Maven 将从本地资源库获得 Maven 的本地资源库依赖资源,如果没有找到,然后把它会从默 ...
- TCP/IP详解学习笔记(9)-- 广播,多播,IGMP:网际组管理协议
1.概述 IP有三种地址:单播地址, 广播地址,多播地址. 广播和多播仅应用于UDP. 每个以太网帧包含源主机和目的主机的以太网地址.通常每个以太网帧发往单个目的主机,目 ...
- 内存泄漏检测工具Valgrind
1概述 1.1 介绍 Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合.Valgrind由内核(core)以及基于内核的其他调试工具组成.内核类似于一个框架(fram ...
- Android开发如何去除标题栏title
虽然是一个小问题,今天遇到了,也就写下来吧.防止自己忘掉. 取消标题栏的方式有两种,一种是在代码添加,另一种是在AndroidManifest.xml里面添加. 1.在代码中实现:在此方法setCon ...
- WebApi简单使用
一.建立一个WebApi项目 WebApi项目的文件和MVC的基本项目内容差不多,都有Models View Controller等,区别在于WebApi的控制器继承的不是Controller类,而是 ...
- solr5.5教程-solr.home 配置
solr/home是solr实例化core核的依据和入口,是必不可少的配置. 1.在web.xml中设置 <env-entry> <env-entry-name>solr/ho ...
- Bootstrap CSS 描述
<!DOCTYPE html><html lang="zh-CN"><head> <!--定于内容,和内容的编码格式--> < ...
- ado.net工厂模式DbProviderFactories
DbProviderFactory f = DbProviderFactories.GetFactory(System.Configuration.ConfigurationManager.Conne ...
- ios球体弹跳游戏源码
一款耐玩的ios游戏源码,画面上有很多小星星,球体落下的时候,你需要在画面上画出一条条的线条让球体弹跳起来然后吃掉小星星,如果没借助球体就失败了.游戏有很多关卡.注意: <ignore_js_o ...