C#装箱与拆箱

 

C#装箱与拆箱

要掌握装箱与拆箱,就必须了解CTS及它的特点。

NET重要技术和基础之一的CTS(Common Type System)。顾名思义,CTS就是为了实现在应用程序声明和使用这些类型时必须遵循的规则而存在的通用类型系统。.Net将整个系统的类型分成两大类 ——Value Type 和 Reference Type。。,多数的OO语言存在这个弱点,原因就是因为他们的原类型没有共同的基点,于是他们在本质上并不是真正的对象C++更依赖于对象,而非面向对象。.Net环境的CTS 给我们带来了方便。第一、CTS中的所有东西都是对象;第二、所有的对象都源自一个基类——System.Object类型。这就是所谓的单根层次结构(singly rooted hierarchy)关于System.Object的详细资料请参考微软的技术文档。CTS  Value Type的一个最大的特点是它们不能为null,Value Type的变量总有一个值。在传递Value Type的变量时,实际传递的是变量的值,而非底层对象的“引用”。CTS  Reference Type就好像是类型安全的指针,它可以为null。当值为null时,说明没有引用或类型指向某个对象。声明一个引用类型的变量时,被操作的是此变量的引用(地址),而不是数据。 

    使用这种多类型系统时如何有效的拓展和提高系统的性能?就是今天探讨的问题,西雅图人提出了Box and UnBox的想法。简言之,装箱就是将value type转换为reference type;反之,就是拆箱。

装箱过程:

第一步将一个值压入堆栈;

第二步将引用类型转换为值类型;

第三步间接将值压栈;第四步传值给dubUnBox。

代码如下:

using System;
namespace Box
{
///
/// BoxAndUnBox
的摘要说明。
///
public class BoxAndUnBox
{
public BoxAndUnBox()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/////////////////////////////////////////////////////////////////////////////////////
static void Main(string[] args)
{
double box1 =11.222; /// 定义一个值形变量
object objBox =box1; /// 将变量的值装箱到 一个引用型对象中
Console.WriteLine("The Value is '{0}' and The Boxed is {1}",box1,objBox.ToString());
}

}
}

打开ildasm.exe

MSIL代码如下:

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//
代码大小 42 (0x2a)
.maxstack 3
.locals init ([0] float64 box1,
              [1] object objBox)
IL_0000: nop

IL_0001: ldc.r8 11.222
IL_000a: stloc.0 //
第IL_0000至IL_000a是定义值型变量的
IL_000b: ldloc.0
IL_000c: box [mscorlib]System.Double
IL_0011: stloc.1     //
第IL_000b 至 IL_0011 行是描述object objBox =box1代码的
IL_0012: ldstr "The Value is '{0}' and The UnBoxed is {1}"
IL_0017: stloc.0
IL_0018: box [mscorlib]System.Double
IL_001d: stloc.1
IL_001e: callvirt instance string [mscorlib]System.Object::ToString()

IL_0023: call void [mscorlib]System.Console::WriteLine(string,
object,
object)

IL_0028: nop
IL_0029: ret
} // end of method BoxAndUnBox::Main

当box1被装箱时所发生的过程:

(1)划分堆栈内存,在堆栈上分配的内存 = box1的大小 + objBox及其结构所占用的空间;(2) box1的值(11.222)被复制到新近分配的堆栈中;

(3)将分配给objBox的地址压栈,此时它指向一个object类型,即引用类型。

拆箱过程:

装箱的逆过程。值得注意以下几点:box time不需要显式的类型转换,在unbox时就必须进行类型转换。因为引用类型的对象可以被转换为任何类型。电脑和人脑一个差别的体现就在于此!哈哈!类型转换不容回避的将会受到来自CTS管理中心的监控——其标准自然是依据规则。

下面这段代码:

using System;
namespace UnBox
{
///
/// BoxAndUnBox
的摘要说明。
///
public class BoxAndUnBox
{
public BoxAndUnBox()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
/////////////////////////////////////////////////////////////////////////////////////
static void Main(string[] args)
{
double box2 = 11.222;
object objBox = box2;
double dubUnBox = (double)objBox; /// 将引用型对象拆箱 ,并返回值
Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",box2,dubUnBox);
}
/////////////////////////////////////////////////////////////////////////////////////
}
}

本段代码多加了一行double dubUnBox = (double)objBox;

这段代码的含义:

第一步将一个值压入堆栈;

第二步将引用类型转换为值类型;

第三步间接将值压栈;

第四步传值给dubUnBox。

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//
代码大小 48 (0x30)
.maxstack 3
.locals init ([0] float64 box1,
[1] object objBox,
[2] float64 dubUnBox)
IL_0000: ldc.r8 77.769999999999996
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: box [mscorlib]System.Double
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: unbox [mscorlib]System.Double
IL_0017: ldind.r8
IL_0018: stloc.2
IL_0019: ldstr "The Value is '{0}' and The UnBoxed is {1}"
IL_001e: ldloc.0
IL_001f: box [mscorlib]System.Double
IL_0024: ldloc.2
IL_0025: box [mscorlib]System.Double
IL_002a: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_002f: ret
} // end of method BoxAndUnBox::Main

//

第IL_0011 至 IL_0018 行是描述double dubUnBox = (double)objBox代码的。

描述一下objBox在拆箱时的情况:(1)环境须先判断堆栈上指向合法对象的地址,以及在对此对象向指定的类型进行转换时是否合法,如果不合法,就抛出异常;(2)当判断类型转换正确,就返回一个指向对象内的值的指针。

改进:

为了避免由于无谓的隐式装箱所造成的性能损失,在执行这些多类型重载方法之前,最好先对值进行装箱。

代码改进:

using System;
namespace NewBU
{
///
/// BoxAndUnBox
的摘要说明。
///
public class BoxAndUnBox
{
public BoxAndUnBox()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
///////////////////////////////////////////////////////////////////
static void Main(string[] args)
{
double box1 = 11.222;
object objBox = box1;
double dubUnBox = (double)objBox;
object objUnBox = dubUnBox;
Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",objBox,objUnBox);
}
///////////////////////////////////////////////////////////////////
}
}  

posted @ 2007-10-03 12:39  quzhixun  阅读(7571)  评论(8编辑  收藏  举报