老话题装箱与拆箱

装箱和取消装箱使值类型能够被视为对象。对值类型装箱将把该值类型打包到 Object 引用类型的一个实例中。这使得值类型可以存储于垃圾回收堆中。取消装箱将从对象中提取值类型。在此示例中,整型变量 i 被“装箱”并赋值给对象 o

相对于简单的赋值而言,装箱和取消装箱过程需要进行大量的计算。对值类型进行装箱时,必须分配并构造一个全新的对象。次之,取消装箱所需的强制转换也需要进行大量的计算。

装箱用于在垃圾回收堆中存储值类型。装箱是值类型 object 类型或到此值类型所实现的任何接口类型的隐式转换。对值类型装箱会在堆中分配一个对象实例,并将该值复制到新的对象中。

此示例通过装箱将整数变量 i 转换为对象 o。这样,存储在变量 i 中的值就从 123 更改为 456。该示例表明原始值类型和装箱的对象使用不同的内存位置,因此能够存储不同的值。

class TestBoxing 

{

    static void Main()

    {

        int i = 123;

        object o = i;  // implicit boxing

       

        i = 456;  // change the contents of i

 

        System.Console.WriteLine("The value-type value = {0}", i);

        System.Console.WriteLine("The object-type value = {0}", o);

    }

}

 

取消装箱是从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换。取消装箱操作包括:

·                       检查对象实例,确保它是给定值类型的一个装箱值。

·                       将该值从实例复制到值类型变量中。

 

上面是MSDN的例子,可以看出它们是很简单的。

装箱是值类型到object类型的转换,它与类型转换有很大的不同,类型转换是由一个类型到其他任何一种类型的转换。如int a=10;byte b =(byte)a; classa c=new classa();object o=c;这些都是类型转换而不是装箱操作,装箱操作有一个要点就是,一定是由值类型向object 类型的转换。

要想真正学会这方面的知识,要知道什么是值类型,有哪几种,在方法调用的时候传入的参数是什么类型的,微软为减少装箱与拆箱操作提供了什么样的便利,在我们编程的时候要注意到哪些问题。下面一一解决:

1、什么是值类型:

值类型主要由两类组成:

·                 结构

·                 枚举

结构分为以下几类:

·                 Numeric(数值)类型

·                                 整型

·                                 浮点型

·                                 decimal

·                 bool

·                 用户定义的结构。

基于值类型的变量直接包含值。将一个值类型变量赋给另一个值类型变量时,将复制包含的值。这与引用类型变量的赋值不同,引用类型变量的赋值只复制对对象的引用,而不复制对象本身。所有的值类型均隐式派生自System.ValueType

与引用类型不同,从值类型不可能派生出新的类型。但与引用类型相同的是,结构也可以实现接口。

与引用类型不同,值类型不可能包含 null 值。然而,可空类型功能允许将 null 赋给值类型。

每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。

与引用类型区分开来。

引用类型的变量又称为对象,可存储对实际数据的引用。本节介绍以下用于声明引用类型的关键字

·                 class

·                 interface

·                 delegate

内置引用类型:

·                 object

·                 string

注意:datetime类型跟string很像,不过它是值类型。对datetime变量的修改如:

DateTime t= DateTime.Now;

Console.Write(t.ToLongDateString());

DateTime t2 = t;

t2.AddDays(5);

Console.Write(t2.ToLongDateString());//t2不变

Console.Write(t.ToLongDateString());//t也不会变因为它是值类型的。

Console.Write(t2.AddDays(5));//打印出的日期改变了。但是t2始终是t2

//通过以下方法改变t2

T2=t2.AddDays(5);

 

2、在方法调用的时候传入的参数是什么类型的

举个例子

        static void Main()

        {

            Print("geege");//不会发生装箱操作,因为string类型为引用类型。

            Print(12);//发生装箱操作因为12为值类型,而Print只接收object类型

            int i = 10;

            object o = i;//这个很明显是装箱操作

            Print(o);//这里不会被装箱了,因为它是引用类型,也不会发生类型转换。

            Console.Read();

        }

        static void Print(object o)

        {

           Console.WriteLine(o);

        }

中间代码可以看出:

.method private hidebysig static void  Main() cil managed

{

  .entrypoint

  // 代码大小       49 (0x31)

  .maxstack  1

  .locals init ([0] int32 i,

           [1] object o)

  IL_0000:  nop

  IL_0001:  ldstr      "geege"

  IL_0006:  call       void test.Test::Print(object)

  IL_000b:  nop

  IL_000c:  ldc.i4.s   12

  IL_000e:  box        [mscorlib]System.Int32

  IL_0013:  call       void test.Test::Print(object)

  IL_0018:  nop

  IL_0019:  ldc.i4.s   10

  IL_001b:  stloc.0

  IL_001c:  ldloc.0

  IL_001d:  box        [mscorlib]System.Int32

  IL_0022:  stloc.1

  IL_0023:  ldloc.1

  IL_0024:  call       void test.Test::Print(object)

  IL_0029:  nop

  IL_002a:  call       int32 [mscorlib]System.Console::Read()

  IL_002f:  pop

  IL_0030:  ret

} // end of method Test::Main

改进方法:

        static void Main()

        {

            Print(12);

            int i = 10;

            Print(i);//这里调用重载方法Print(int i)没有发生装箱操作,所以就避免了装箱操作的发生。

            Console.Read();

        }

        static void Print(int i)

        {

            Console.WriteLine(i);// Console.WriteLine能接收int类型所以没有发生装箱操作

        }

        static void Print(object o)

        {

            Console.WriteLine(o);

        }

3、微软为减少装箱与拆箱操作提供了什么样的便利

首先是MS 为我们提供了丰富的类库和足够多的重载方法来避免装箱操作,其次就是它提供的支持泛型操作。在编程的时候,我们尽量也为我们的设计提供实用的重载方法来避免装箱操作,如果能写出泛型方法就更好了。MSLINQ就全程提供了泛型操作,不过要写出漂亮的泛型方法还是得好好看一下MSND的。关于泛型在以后会写出来,因为它的设计涉及到很多限制,对这种限制掌握的越全面就越能写出好的泛型方法。

4、在我们编程的时候要注意到哪些问题

在拆箱时要正确的转换类型。

static void Print(Array ilist)

        {

            foreach (int i in ilist )

            {

                Console.WriteLine(i);

            }

        }

        static void Print2(Array ilist)

        {

            foreach (object i in ilist)

            {

                Console.WriteLine(i);

            }

        }

上面的例子没有什么意义但说明了一点:

当调用Print(new int[] { 1, 2, 3, 4 });时第一个方法会出现拆箱操作,第二个方法中不会出现这种情况,所以在设计程序时一定要注意这些东西。

posted @ 2010-05-31 10:02  gege_s  Views(310)  Comments(0)    收藏  举报