装箱(Boxing)和拆箱(Unboxing)

转载 2008年09月28日 10:51:00
首先介绍装箱(Boxing)和拆箱(Unboxing)这两个名词。.Net的类型分为两种,一种是值类型,另一种是引用类型。这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。

<?XML:NAMESPACE PREFIX = O />

 

例如,对于如下简单的装箱和拆箱操作语句。

    int i = 123;

    object obj = i;//Boxing

 

    if( obj is int )

        int  j = (int) obj;//Unboxing

 

为了,更好的诠释装箱和拆箱操作,我借用MSDN关于“Boxing”的解释图,具体如下。

<?XML:NAMESPACE PREFIX = V />

 

明白了这两名词的意思,现在说说为什么要减少装箱和拆箱操作。

原因有两个,主要是关于效率:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要GC来回收,从而降低程序效率。

 

考虑到这两点因素,那么需要在程序中减少装箱和拆箱操作。

如何减少呢,涉及到这两个操作比较多的是,格式化输出操作,例如:String.FormatConsole.WriteLine之类的语句。

例如:

    Console.WriteLine( "Number list:{0}, {1}, {2}",

        1,2,3 );

 

对于“123”来说,相当于前面的“123”一样,需要经过装箱和拆箱两个操作。那么如何避免呢,其实只要向WriteLine传递引用类型数据即可,也就是按照如下的方式。

    Console.WriteLine( "Number list:{0}, {1}, {2}",

        1.ToString(),2.ToString(),3.ToString() );

 

由于“1.ToString()”的结果是String类型,属于引用类型,因此不牵扯装箱和拆箱操作。

 

其次,牵扯到装箱和拆箱操作比较多的就是在集合中,例如:ArrayList或者HashTable之类。

把值类型数据放到集合中,可能会出现潜在错误。例如:

    public struct Person

    {

        private string _Name;

        public string Name

        {

            get{ return _Name; }

            set{ _Name = value; }

        }

 

        public Person( string PersonName )

        {

            _Name = PersonName;

        }

 

        public override string ToString()

        {

            return _Name;

        }

 

    }

 

    ArrayList arrPersons = new ArrayList();

    Person p = new Person( "OldName" );

    arrPersons.Add( p );

   

   

    p = ( Person ) arrPersons[0] ;

    p.Name = "NewName";

 

    Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "OldName"

 

这个问题其实在前面的文章中已经讲过了。有人可能会说,是否可以按照如下的方式去修改呢。

( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled

 

很不幸,如上操作不能通过编译。为什么呢,对于“( (Person ) arrPersons[0] )”来说,是系统用一个临时变量来接收拆箱后的值类型数据,那么由于值类型是分配在栈上,那么操作是对实体操作,可是系统不允许对一个临时值类型数据进行修改操作。

   

    ArrayList arrPersons = new ArrayList();

    Person p = new Person( "OldName" );

    arrPersons.Add( p );

   

    // Try to change the name

    p = ( Person ) arrPersons[0] ;

    p.Name = "NewName";

 

    arrPersons.RemoveAt( 0 );//Remove old data first

    arrPersons.Insert( 0, p );//Add new data

    Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"

 

其实,这样操作会产生过多装箱和拆箱操作。那么更好的方法,可以通过接口来完成,从而减少装箱和拆箱操作。对于这个例子的接口实现应该如下。

    public interface IPersonName

    {

        string Name{ get;set;}

    }

 

    public struct Person:IPersonName

    {

        private string _Name;

        public string Name

        {

            get{ return _Name; }

            set{ _Name = value; }

        }

 

        public Person( string PersonName )

        {

            _Name = PersonName;

        }

 

        public override string ToString()

        {

            return _Name;

        }

    }

 

      

    ArrayList arrPersons = new ArrayList();

    Person p = new Person( "OldName" );

    arrPersons.Add( p );

   

    // Change the name

    ( (IPersonName)arrPersons[0] ).Name = "NewName";

 

    Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"

 

很多人就问,为什么值类型不能修改,即

( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled

而如上的接口类型就能修改呢,即

( (IPersonName)arrPersons[0] ).Name = "NewName";

这是由于产生的临时变量的类型不同,前者已经在前面进行说明了,后者由于产生的临时变量的类型为IPersonName,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。

 

通过接口来改写,这样就减少了装箱和拆箱操作,同时也保证了修改的正确性。不过要注意的是,这里接口对于的是引用类型,如果接口访问的或者返回的是值类型,那么用接口虽说能实现了,但是对于装箱和拆箱操作来说,并没有减少。

 

对于装箱和拆箱操作来说,基本上就讲完了,只要记住频繁装箱和拆箱操作会降低程序效率,因此在编写的时候要尽量避免。  

C#装箱和拆箱(Boxing 和 UnBoxing)

1、什么是装箱和拆箱? 简单来说:       装箱是将值类型转换为引用类型 ;拆箱是将引用类型转换为值类型。(网上广为流传)  C#中值类型和引用类型的最终基类都是Object类型(它本身是一个引用...
  • qiaoquan3
  • qiaoquan3
  • 2016年05月17日 21:44
  • 2673

装箱和拆箱的基本原理

3.1.4 简述装箱和拆箱原理 40 · 装箱和拆箱的基本概念 · 装箱拆箱对性能的影响 · 如何有效避免装箱拆箱   分析问题 1.装箱和拆箱的基本概念 在第3.1.3节中,笔者已经介...
  • szyzxcv5689
  • szyzxcv5689
  • 2013年04月21日 16:31
  • 2669

关于装箱拆箱为什么会影响效率

概念 装箱在值类型向引用类型转换时发生,在堆中分配。 拆箱在引用类型向值类型转换时发生。 示例装箱 public void BoxIn() { ...
  • Admin_Jhon
  • Admin_Jhon
  • 2016年10月20日 15:55
  • 763

深入剖析Java中的装箱和拆箱

原文出处: 海子 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱、拆箱相...
  • zf0512305
  • zf0512305
  • 2016年03月19日 14:39
  • 4128

详解Java的自动装箱与拆箱(Autoboxing and unboxing)

一、什么是自动装箱拆箱 很简单,下面两句代码就可以看到装箱和拆箱过程//自动装箱 Integer total = 99;//自定拆箱 int totalprim = total;简单一点说,装箱就是...
  • hp910315
  • hp910315
  • 2015年09月22日 15:27
  • 4760

Java学习笔记之自动装箱与拆箱

更多博文可参考我的个人独立博客:贱贱的梦想 什么是自动装箱与拆箱自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Intege...
  • GongchuangSu
  • GongchuangSu
  • 2016年05月28日 22:28
  • 1100

深入剖析Java中的装箱和拆箱

深入剖析Java中的装箱和拆箱   自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的...
  • carolzhang8406
  • carolzhang8406
  • 2015年05月27日 14:07
  • 2955

浅C#中的装箱和拆箱

1、什么是装箱和拆箱? 简单的来说: 装箱就是值类型转换为引用类型;拆箱就是引用类型转换为值类型 值类型,包括原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、...
  • gengyudan
  • gengyudan
  • 2013年06月21日 09:53
  • 6611

C# - 再谈C#的装箱和拆箱

上一篇写了一下装箱拆箱的定义和IL分析,这一篇我们看下使用泛型和不使用泛型引发装箱拆箱的情况 1. 使用非泛型集合时引发的装箱和拆箱操作  看下面的一段代码: 1 2 3 ...
  • honey199396
  • honey199396
  • 2016年08月31日 11:35
  • 504

C#--三行代码带你理解神秘的拆箱和装箱

一、在说拆箱和装箱之前的准备知识首先,我们需要知道c#中有两种类型:值类型和引用类型 名称 值类型 引用类型 表示类型 基本类型 类,数组,接口 ,C#特有的委托. 存储内容...
  • qq_32452623
  • qq_32452623
  • 2016年12月29日 17:22
  • 2455
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:装箱(Boxing)和拆箱(Unboxing)
举报原因:
原因补充:

(最多只允许输入30个字)