c#-装箱-拆箱


 

装箱是将值类型转换为 object 类型或由此值类型实现的任一接口类型的过程。 当 CLR 对值类型进行装箱时,会将该值包装到 System.Object 内部,再将后者存储在托管堆上。 取消装箱将从对象中提取值类型。 装箱是隐式的;取消装箱是显式的。 装箱和取消装箱的概念是类型系统 C# 统一视图的基础,其中任一类型的值都被视为一个对象。



把不同类型转换成ob类。就是装箱。拆箱是把oj类转换其他类。
如string  i=“50”;
object s=i;//装箱
int j=(int)i;//拆箱
拆箱要小心转换。但是比这个更好的有泛型

 

要掌握装箱与拆箱,就必须了解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 on 2012-04-10 16:07 青蛙學堂 阅读(370) 评论(2)  编辑 收藏 引用 所属分类: Vs2008

评论

# re: c#-装箱-拆箱 2012-04-10 16:11 uuu

 C#语言还是比较常见的东西,这里我们主要介绍C#装箱和拆箱,包括介绍调用该 TestAlias() 函数等方面。

  C#装箱和拆箱还是别名

  许多 C#.net 的书上都有介绍 int -> Int32 是一个装箱的过程,反之则是拆箱的过程。许多其它变量类型也是如此,如:short <-> Int16,long <-> Int64 等。对于一般的程序员来说,大可不必去了解这一过程,因为这些C#装箱和拆箱的动作都是可以自动完成的,不需要写代码进行干预。但是我们需要记住这些类型之间的关系,所以,我们使用“别名”来记忆它们之间的关系。

  C# 是全面向对象的语言,比 Java 的面向对象都还彻底——它把简单数据类型通过默认的装箱动作封装成了类。Int32、Int16、Int64 等就是相应的类名,而那些我们熟悉的、简单易记的名称,如 int、short、long 等,我们就可以把它称作是 Int32、Int16、Int64 等类型的别名。

  那么除了这三种类型之外,还有哪些类有“别名”呢?常用的有如下一些:

  ◆bool -> System.Boolean (布尔型,其值为 true 或者 false)

  ◆char -> System.Char (字符型,占有两个字节,表示 1 个 Unicode 字符)

  ◆byte -> System.Byte (字节型,占 1 字节,表示 8 位正整数,范围 0 ~ 255)

  ◆sbyte -> System.SByte (带符号字节型,占 1 字节,表示 8 位整数,范围 -128 ~ 127)

  ◆ushort -> System.UInt16 (无符号短整型,占 2 字节,表示 16 位正整数,范围 0 ~ 65,535)

  ◆uint -> System.UInt32 (无符号整型,占 4 字节,表示 32 位正整数,范围 0 ~ 4,294,967,295)

  ◆ulong -> System.UInt64 (无符号长整型,占 8 字节,表示 64 位正整数,范围 0 ~ 大约 10 的 20 次方)

  ◆short -> System.Int16 (短整型,占 2 字节,表示 16 位整数,范围 -32,768 ~ 32,767)

  ◆int -> System.Int32 (整型,占 4 字节,表示 32 位整数,范围 -2,147,483,648 到 2,147,483,647)

  ◆long -> System.Int64 (长整型,占 8 字节,表示 64 位整数,范围大约 -(10 的 19) 次方 到 10 的 19 次方)

  ◆float -> System.Single (单精度浮点型,占 4 个字节)

  ◆double -> System.Double (双精度浮点型,占 8 个字节)

  我们可以用下列代码做一个实验:

private void TestAlias() {  
// this.textBox1 是一个文本框,类型为 System.Windows.Forms.TextBox  
// 设计中已经将其 Multiline 属性设置为 true  
byte a = 1; char b = 'a'; short c = 1;  
int d = 2; long e = 3; uint f = 4; bool g = true;  
this.textBox1.Text = "";  
this.textBox1.AppendText("byte -
>
 " + a.GetType().FullName + "\n");  
this.textBox1.AppendText("char -
>
 " + b.GetType().FullName + "\n");  
this.textBox1.AppendText("short -
>
 " + c.GetType().FullName + "\n");  
this.textBox1.AppendText("int -
>
 " + d.GetType().FullName + "\n");  
this.textBox1.AppendText("long -
>
 " + e.GetType().FullName + "\n");  
this.textBox1.AppendText("uint -
>
 " + f.GetType().FullName + "\n");  
this.textBox1.AppendText("bool -
>
 " + g.GetType().FullName + "\n");  
} 

  在窗体中新建一个按钮,并在它的单击事件中调用该 TestAlias() 函数,我们将看到运行结果如下:

byte -
>
 System.Byte  
char -
>
 System.Char  
short -
>
 System.Int16  
int -
>
 System.Int32  
long -
>
 System.Int64  
uint -
>
 System.UInt32  
bool -
>
 System.Boolean 




  回复  更多评论   

只有注册用户登录后才能发表评论。
<2014年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

统计

常用链接

留言簿(8)

随笔分类

随笔档案

收藏夹

青蛙学堂

最新评论

阅读排行榜

评论排行榜