装箱与拆箱

目录

  • 生活中的装箱与拆箱
  • C# 的装箱与拆箱
  • 值类型和引用类型
  • 装箱
  • 拆箱
  • 读者见解

生活中的装箱与拆箱

     我们习惯了在网上购物,这次你想买本编程书 -- 《C 语言从入门到放弃》 ,下单成功后,卖家会帮你将这本入坑指南打好包装,我们可以称之为装箱;经过快递员的快马加鞭,风雨无阻,包裹就直接送到你手上了。你一定会以迅雷不及掩耳盗铃儿响叮当之势拆开包装,这个过程我们可以称之为拆箱,这时,入坑指南就顺利的送到你手上。

C# 的装箱与拆箱

  装箱:将值类型(如 int ,或自定义的值类型等)转换成 object 或者接口类型的一个过程。当 CLR 对值类型进行装箱时,会将该值包装为 System.Object 类型,再将包装后的对象存储在堆上。 拆箱就是从对象中提取对应的值类型的一个过程。

  装箱是隐式的;拆箱必定是显式的。

  与简单的赋值操作相比,装箱和拆箱都需要进行大量的数据计算。对值类型进行装箱时,CLR 必须重新分配一个新的对象。拆箱所需的强制转换也需要进行大量的计算,两者相比,仅仅是程度不高,并且也可能会出现类型转换发生的异常情形。如果你的操作正处于循环的中心,通过测试(如:Stopwatch),你会很明显的感觉到性能问题。

  .NET 2.0 引入的泛型其实在很大的程度上解决了装拆箱产生的类型转换问题,也减少了类型转换所引起的运行时的异常,及保证了类型安全,从而提高了性能。
        static void Main(string[] args)
{
var i = ; //System.Int32 //对 i 装箱(隐式)
object obj = i; //对 obj 进行拆箱(显式)
i = (int)obj; Console.Read();
}

  在这里,我先将变量 i (int 类型)进行了装箱,并分配给对象 obj。其次,再次将对象 obj 进行拆箱(即强转)并重新给变量 i(int 类型)赋值。

  直接通过反编译得到的 IL 代码,从 box 和 unbox 这两个指令也可以看出具体在哪一步发生装箱和拆箱操作。

值类型和引用类型

  值类型和引用类型,这两者本来没有多大的联系(可能就是基类为 object),设计人员通过一种名为装拆箱的操作使得这两种类型创建了新的联系,让任何值类型都可以当成对象(引用)类型来进行操作。

  装拆箱其实就是值类型和引用类型两者之间的类型转换操作。这里,我简单梳理一下这两种类型:

  (1)值类型:整型:Int;长整型:long;浮点型:float;字符型:char;布尔型:bool;枚举:enum;结构:struct;它们统一继承  System.ValueType。

  (2)引用类型:数组,用户定义的类、接口、委托,object,字符串等。

  (3)简单的堆栈图:

图:@ 表示一个引用地址

装箱

  装箱就是值类型到 object 类型或者到该值类型所实现的接口类型所实现的一个隐式转换过程(可显式)。装箱的时候会在堆中自动创建一个对象实例,然后将该值复制到新对象内。

    var i = ;    //System.Int32

    //对 i 装箱(隐式)进对象 o
object o = i;

  

  从图可知,对象 o 存的是地址引用,指向的是堆上的值,这个值的类型和变量 i 一样,也是 int 类型,值(123)也就是从变量 i Copy 过来的一个副本值而已。

  【备注】装箱默认是隐式的,当然,你可以选择显式,但这并不是必须的。

拆箱

  拆箱是从 object 类型到值类型,或从接口类型到实现该接口的值类型的显式转换的一个过程。

  拆箱:检查对象实例,确保它是给定值类型的一个装箱值后,再将该值从实例复制到值类型变量中。

    int i = ;      // 值类型
object o = i; // 装箱
int j = (int)o; // 拆箱

  要在运行时成功拆箱值类型,被拆箱的项必须是对一个对象的引用,该对象是先前通过装箱该值类型的实例创建的。

  

  拆箱时需要注意,转换出现异常的情形:

  虽然,decimal 类型可以直接强转为 int 类型,但从调式的结果来看,拆箱时是会引发“转换无效”的异常。要记住,拆箱时强转的值类型,应以装箱时的值类型一致。

读者见解

   深蓝医生:简单说,装箱就是把值类型变成引用类型使用;拆箱就是将引用类型变成值类型使用。然而,大量使用值类型会引起变量值的大量拷贝,反而降低运行效率。所以装箱没有那么可怕,这可以通过 EF的code first和SOD框架的code first代码进行测试(要有业务层代码这种),虽然SOD框架的实体类看起来都是“装箱”过的,但是它的性能不会输给EF。

  lulianqi15:最后加的一句注意(decimal 类型可以直接强转为 int 类型........应以装箱时的值类型一致),其实不太严谨,decimal 128位,想想都不可能无缘无故转换成32位的数据,之所以能强制转换,是因为Decimal 自己实现了自定义强制转换public static explicit operator int(decimal value)。回到最后例子的报错,JIT肯定是知道obj是Decimal(因为Decimal数据移动到托管堆上后后还额外为其添加了类型对象指针及同步块索引,所以即使obj在ide里申明为object,不过jit是知道他就是Decimal)之所以发生异常的原因是CLR认为在生成il时就认为obj是object类型,而object没有实现explicit 指定重载(当然可以自己实现)。所以就调用了object默认的强制转换,检查类型指针的时候发现不合法就报错了,那如果认可Decimal可以强制转换为int,说到底最后在强制转换报错的根本原因也只是object没有实现explicit 指定重载。如果自定义类型自己实现了explicit,那在转换时也不用保证其运行时类型与要转换的类型一致。

「如果不想在世界上虚度一生,那就要学习一辈子。」-- 高尔基


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6486332.html

【参考】微软官方文档 MSDN

C# 知识回顾 - 装箱与拆箱的更多相关文章

  1. [C#] C# 知识回顾 - 装箱与拆箱

    装箱与拆箱 目录 生活中的装箱与拆箱 C# 的装箱与拆箱 值类型和引用类型 装箱 拆箱 读者见解 生活中的装箱与拆箱    我们习惯了在网上购物,这次你想买本编程书 -- <C 语言从入门到放弃 ...

  2. java 基础知识七 装箱和拆箱

    java  基础知识七  装箱和拆箱 数据类型可分为两大种,基本数据类型(值类型)和类类型(引用数据类型) 装箱:把基本类型用他们相对应的引用类型包装起来,使他们可以具有对象的特质    基本数据类型 ...

  3. 有关c#装箱和拆箱知识整理

    c#装箱和拆箱知识,装箱和拆箱是一个抽象的概念. 1.装箱和拆箱是一个抽象的概念  2.装箱是将值类型转换为引用类型 : 拆箱是将引用类型转换为值类型 利用装箱和拆箱功能,可通过允许值类型的任何值与O ...

  4. C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)

    前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...

  5. C#基础回顾(二)—页面值传递、重载与重写、类与结构体、装箱与拆箱

    一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C ...

  6. 从别人那淘的知识 深入剖析Java中的装箱和拆箱

    (转载的海子的博文   海子:http://www.cnblogs.com/dolphin0520/) 深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来 ...

  7. C#:装箱和拆箱相关知识整理

    1.装箱和拆箱是一个抽象的概念   2. 装箱是将值类型转换为引用类型 ;   拆箱是将引用类型转换为值类型   利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将值类型 ...

  8. 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱

    为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...

  9. .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)

    作者: Edison Chou  来源: 博客园  发布时间: 2014-09-03 15:59  阅读: 318 次  推荐: 2   原文链接   [收藏]   原文作者:Shivprasad k ...

随机推荐

  1. Android项目文件结构

    一.Project项目结构 app/build/ app模块build编译输出的目录 app/build.gradle app模块的gradle编译文件 app/app.iml app模块的配置文件 ...

  2. DataTable.DataRow的复制

    经常遇到这种错误,“此行已属于另一个表”的错误,导致这个错误的语句如下: dtPriceTable.Rows.InsertAt(aDataRow,i); 或者 dtPriceTable.Rows.Ad ...

  3. 转化成maven dependencis

    右键工程--->configure--->convert to maven project

  4. C# windows服务的创建与调试

    Windows Service这一块并不复杂,但是注意事项太多了,网上资料也很凌乱,偶尔自己写也会丢三落四的.所以本文也就产生了,本文不会写复杂的东西,完全以基础应用的需求来写,所以不会对Window ...

  5. redhat5安装phantomjs

    Linux 64-bit Download phantomjs-2.1.1-linux-x86_64.tar.bz2 (22.3 MB) and extract the content. Note: ...

  6. linux下两台服务器文件实时同步方案设计和实现

    inux下两台服务器文件实时同步方案设计和实现 假设有如下需求: 假设两个服务器: 192.168.0.1 源服务器  有目录 /opt/test/ 192.168.0.2 目标服务器  有目录 /o ...

  7. easyui弹出窗关闭前调用确认窗口,先关闭页面后调用弹出窗口

    弹出窗关闭的时候提示是否关闭,同时进行一些对应的方法调用, 然而在进行页面关闭调用的时候,往往页面关闭了,才弹出确认对话框, $.messager.confirm和panel的onBeforeClos ...

  8. redhat6.4下安装Oracle11g

    一.在Root用户下执行以下步骤: 1)修改用户的SHELL的限制,修改/etc/security/limits.conf文件 *               soft    nproc  2047 ...

  9. ASP.NET Zero--13.一个例子(6)商品分类管理-删除分类

    1.添加按钮 首先添加一个删除按钮,打开文件Index.js[..\MyCompanyName.AbpZeroTemplate.Web\Areas\Mpa\Views\Category\Index.j ...

  10. objectc中函数前的加号和减号

    看object-c中的代码里,函数(方法)前总有一个加号或者减号,不知道是什么意思,度娘了一下. http://zhidao.baidu.com/link?url=gw9-JR3bh0i7E_CHbr ...