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 阅读(1471)
评论(2) 编辑 收藏 引用