posts - 1,comments - 2,trackbacks - 0

C++/CLI与C#中的装箱、拆箱的最大区别在于C++/CLI装箱后能得到强类型的对象,而C#则只能将值类型装箱为Object或ValueType,以下将先分别介绍两种语言的装箱拆箱操作,最活再进行综合的比较。
C#的装箱与拆箱

1.         最简单的装箱

Int32 a = 100;

Object o = a;

产生的 IL 指令如下

  IL_0001:  ldc.i4.s   100

  IL_0003:  stloc.0

  IL_0004:  ldloc.0

  IL_0005:  box        [mscorlib]System.Int32

  IL_000a:  stloc.1

   这里的装箱是显式的,有时则会进行隐式的装箱,例如

       Int32 a = 100;

       Console.WriteLine(a.ToString());// 此不会装箱

       Console.WriteLine(a.GetType());// 装箱

生成的IL指令如下

       IL_0004:  ldloca.s   V_0

  IL_0006:  call       instance string [mscorlib]System.Int32::ToString()

  IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0010:  nop

  IL_0011:  ldloc.0

  IL_0012:  box        [mscorlib]System.Int32

  IL_0017:  call       instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()

  IL_001c:  call       void [mscorlib]System.Console::WriteLine(object)

因为Int32重写了ToStrting方法,因此调用该方法时不会装箱,而调用GetType时则必须调用其基类Object的方法,因此必须装箱。

2.         改变已装箱的对象值

先定义一值类型的 Point

public struct Point

{

       public Int32 X;

       public Int32 Y;

}

再看以下操作

Point pt;

              pt.X = 100;

              pt.Y = 100;

              Object o = pt;// 装箱

              Point temp = (Point)o;// 拆箱

              temp.X = 10;

              temp.Y = 20;

              Console.WriteLine(((Point)o).X.ToString()+"\n"+((Point)o).Y.ToString());// 两次拆箱

              o = temp// 装箱

       Console.WriteLine(((Point)o).X.ToString()+"\n"+((Point)o).Y.ToString());// 两次拆箱

第一次输出结果为 100 100 ,第二次输出结果为 10 20

产生的 IL 指令如下

  ……

IL_0013:  ldloc.0

  IL_0014:  box        Point                     // 第一次装箱

  IL_0019:  stloc.1

  IL_001a:  ldloc.1

  IL_001b:  unbox.any  Point                    // 第一次拆箱

  IL_0020:  stloc.2

  IL_0021:  ldloca.s   V_2

  IL_0023:  ldc.i4.s   10

  IL_0025:  stfld      int32 Point::X

  IL_002a:  ldloca.s   V_2

  IL_002c:  ldc.i4.s   20

  IL_002e:  stfld      int32 Point::Y

  IL_0033:  ldloc.1

  IL_0034:  unbox.any  Point                   // 输出时的拆箱

  IL_0039:  ldfld      int32 Point::X

  IL_003e:  stloc.3

  IL_003f:  ldloca.s   V_3

  IL_0041:  call       instance string [mscorlib]System.Int32::ToString()

  IL_0046:  ldstr      "\n"

  IL_004b:  ldloc.1

  IL_004c:  unbox.any  Point                   // 输出时的拆箱

  IL_0051:  ldfld      int32 Point::Y

  IL_0056:  stloc.3

  IL_0057:  ldloca.s   V_3

  IL_0059:  call       instance string [mscorlib]System.Int32::ToString()

  IL_005e:  call       string [mscorlib]System.String::Concat(string,

                                                              string,

                                                              string)

  IL_0063:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0068:  nop

  IL_0069:  ldloc.2

  IL_006a:  box        Point                   // 第二次装箱

  ……

3.         通过接口改变已装箱对象的值

装箱后,由于对象不具有强类型,无法知道对象内部的结构,对象成员的改变,必须经过先拆箱,改变拆箱对象的值,最后再重新装箱这三步。以下将通过接口实现来改变已装箱对象的值。

public interface IChange

{

     void Change(Int32 x,Int32 y);

}

public struct Point : IChange

{

     public Int32 X;

     public Int32 Y;

     public void Change(Int32 x,Int32 y)

     {

            this.X = x;

            this.Y = y;

     }

}

public class MainClass

{

     static void Main()

     {

            Point pt;

            pt.X = 100;

            pt.Y = 100;

            Object o = pt;

            ((IChange)o).Change(50,50);

            Console.WriteLine(((Point)o).X);

     }

}

通过接口改变已装箱的值,虽然减少了拆箱和装箱所带来的性能损失,但是需要值类型实现接口。

再来看看 C++/CLI 中的装箱与拆箱

       Point pt;

       pt.X = 100;

       pt.Y = 100;

       Point^ ptrPt = pt;// 装箱

       Console::WriteLine(ptrPt->X.ToString()+"\n"+ptrPt->Y.ToString());// 这里进行了两次拆箱

       ptrPt->X = 90// 拆箱

       ptrPt->Y = 50;// 拆箱

       Console::WriteLine(ptrPt->X.ToString()+"\n"+ptrPt->Y.ToString());

虽然 C++/CLI 对象具有强类型,但事实上,对已装箱的对象还是必须经过拆箱。只不过对 C++/CLI 程序员来说,看到的只是对对象成员的一次赋值,实际的操作都由编译器自动生成 IL 指令来完成。就上述改变坐标操作操作而言,产生的代码如下:

IL_005f:  ldloc.0

  IL_0060:  unbox      Point

  IL_0065:  ldc.i4.s   90

  IL_0067:  stfld      int32 Point::X

  IL_006c:  ldloc.0

  IL_006d:  unbox      Point

  IL_0072:  ldc.i4.s   50

  IL_0074:  stfld      int32 Point::Y

  IL_0079:  ldloc.0

  IL_007a:  unbox      Point

  IL_007f:  ldfld      int32 Point::Y

  IL_0084:  stloc.3

  IL_0085:  ldloc.0

  IL_0086:  unbox      Point

  IL_008b:  ldfld      int32 Point::X

  IL_0090:  stloc.2

疑问: C++/CLI 中的装箱是真正的强类型么?也就是说真正了解了对象的内部构造么?如果是为何对对象的改变必须经过拆箱操作?

以上 IL 指令为何只有拆箱而没有相对应的封箱操作呢?

结论:虽然C++/CLI的装箱可以得到强类型的对象,但改变该对象还是要经过拆箱处理。

posted on 2006-07-10 20:05 Standers 阅读(1448) 评论(2)  编辑 收藏 引用

FeedBack:
# re: C++/CLI与C#中的装箱、拆箱比较
2006-07-14 15:42 | wbt
极其讨厌.net天天挂在嘴上的装箱、拆箱,事件,委托
把非常简单的一些事情给复杂化  回复  更多评论
  
# re: C++/CLI与C#中的装箱、拆箱比较
2006-07-20 19:26 | acme
嗯,知其然不知其所以然确实让糊涂人过的很快活  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理