C# 知识回顾 - 装箱与拆箱

装箱与拆箱

目录

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

生活中的装箱与拆箱

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

C# 的装箱与拆箱

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

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

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

  .NET 2.0 引入的泛型其实在很大的程度上解决了装拆箱产生的类型转换问题,也减少了类型转换所引起的运行时的异常,从而提高了性能。

        static void Main(string[] args)
        {
            var i = 123;    //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 = 123;    //System.Int32

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

  

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

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

拆箱

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

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

    int i = 123;      // 值类型
    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,那在转换时也不用保证其运行时类型与要转换的类型一致。

时间: 2024-10-10 21:51:20

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

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

(转载的海子的博文   海子:http://www.cnblogs.com/dolphin0520/) 深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱.拆箱相关的问题. 以下是本文的目录大纲: 一.什么是装箱?什么是拆箱? 二.装箱和拆箱是如何实现的 三.面试中相关的问题 原文浏览地址 http://www.cnblogs.com/dolphi

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

一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C#提供一下几种方式: 1.Query.String方式 2.Server.Transfer方式 3.Cookie方式 4.Session方式 5.Application方式 (2)实现方式 新建TestTransfer项目,添加TransferOne.aspx页面和TransferTwo.aspx页

温习.net基础知识(二)装箱和拆箱

内存机制 数据在内存中分配位置取决与该变量的数据类型,上图可知值类型分配在线程的堆栈上,引用类型则分配在托管堆上,由GC控制回收,以下代码和图演示了引用类型和值类型的区别: private static class ReferenceVsValue {       // Reference type (because of 'class')       private class SomeRef { public Int32 x; } // Value type (because of 'str

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

作者: Edison Chou  来源: 博客园  发布时间: 2014-09-03 15:59  阅读: 318 次  推荐: 2   原文链接   [收藏] 原文作者:Shivprasad koirala 英文原文:Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing 一.概述 本文会阐述六个重要的概念:堆.栈.值类型.引用类型.装箱和拆箱.本文首先会通过阐述当

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

内容导读 概述 当你声明一个变量背后发生了什么? 堆和栈 值类型和引用类型 哪些是值类型,哪些是引用类型? 装箱和拆箱 装箱和拆箱的性能问题 一.概述 本文会阐述六个重要的概念:堆.栈.值类型.引用类型.装箱和拆箱.本文首先会通过阐述当你定义一个变量之后系统内部发生的改变开始讲解,然后将关注点转移到存储双雄:堆和栈.之后,我们会探讨一下值类型和引用类型,并对有关于这两种类型的重要基础内容做一个讲解. 本文会通过一个简单的代码来展示在装箱和拆箱过程中所带来的性能上的影响,请各位仔细阅读. 二.当你

6个重要的.NET概念栈,堆,值类型,引用类型,装箱,拆箱

介绍 这篇文章将解释6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.本文将会阐述当你声明一个变量时发生了什么并提前说明两个重要个概念:栈和堆.文章将围绕引用类型和值类型澄清一些重要基本信息.并通过一个简单的示例来演示装箱和拆箱引起的性能损失. 声明一个变量时发生了什么? 当你在.NET应用程序中声明了一个变量时,将会从RAM中分配一小块内存,在内存中存在三样东西:变量名称,数据类型和变量的值. 我们简单的说明下在内存中发生了什么,要知道变量在内存中的分配是取决于数据类型的.这里存在两种类型

复习系列之装箱和拆箱

一 装箱和拆箱的概念 装箱是将值类型转换为引用类型 : 拆箱是将引用类型转换为值类型 : 值类型:包括原类型(Sbyte.Byte.Short.Ushort.Int.Uint.Long.Ulong.Char.Float.Double.Bool.Decimal).枚举 (enum) .结构 (struct). 引用类型:包括类.数组.接口.委托.字符串等 我们利用代码说明一下: int n = 10; object obj = n; Console.WriteLine("装箱之前的数字为{0},装

.NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱

一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈的文章,不过写的不深刻,剖析的也不全面,所以今天也参考了一些大牛的资料. 一.预备知识—程序的内存分配    一个由C/C++编译的程序占用的内存分为以下几个部分    1.栈区(stack)—   由编译器自动分配释放   ,存放函数的参数值,局部变量的值等.其    操作方式类似于数据结构中的栈.栈是一个内存数组,是一个LIFO(last-in  f

装箱和拆箱 深度理解

提问: 首先我们来提两个疑问,我们自定义了一个类如Customclass类型, Customclass myclass=new Customclass() Object obj=myclass; 运行上面这段代码,我们会进行装箱操作吗? 基础知识: .Net的类型分为两种,一种是值类型(Value Type ),另一种是引用类型(Reference Type).这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上.那么如果要把一个值类型数据放到堆上,就需要装箱操作:反之,把一个