zoukankan      html  css  js  c++  java
  • 装箱(Boxing)和拆箱(Unboxing)

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

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

        int i = 123;

        object obj = i;//Boxing

     

        if( obj is int )

            int  j = (int) obj;//Unboxing

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

        

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

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

     

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

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

    例如:

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

            1,2,3 );

     

    对于“1,2,3”来说,相当于前面的“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,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。

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

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


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/songxiaozhao/archive/2008/04/01/2235012.aspx 

  • 相关阅读:
    flink 读取kafka 数据,partition分配
    Flink 报错 "Could not find a suitable table factory for 'org.apache.flink.table.factories.StreamTableSourceFactory' in the classpath"
    flume接收http请求,并将数据写到kafka
    【翻译】Flume 1.8.0 User Guide(用户指南) Processors
    【翻译】Flume 1.8.0 User Guide(用户指南) Channel
    【翻译】Flume 1.8.0 User Guide(用户指南) Sink
    【翻译】Flume 1.8.0 User Guide(用户指南) source
    【翻译】Flume 1.8.0 User Guide(用户指南)
    Apache Flink 简单安装
    Java之使用IDE
  • 原文地址:https://www.cnblogs.com/purplefox2008/p/1808892.html
Copyright © 2011-2022 走看看