[CLR via C#]5.3 值类型的装箱和拆箱
在CLR中为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制。
public static void Main(){
Int32 x = ;
Object o = x;
Int16 y = (Int16) o;//抛出异常
}
在对一个对象进行拆箱的时候,只能将其转型为原始未装箱时的值类型——Int32,下面是正确的写法:
public static void Main(){
Int32 x = ;
Object o = x; //对x进行装箱,o引用已装箱的对象
Int16 y = (Int16) (Int32) o; //先拆箱为正确的类型,在进行装箱
}
前面说过,在进行一次拆箱后,经常会紧接着一次字段的复制。以下演示了拆箱和复制操作:
public static void Main() {
Point p = new Point(); //栈变量
p.x = p.y = ;
object o = p; //对p进行装箱,o引用已装箱的实例
p = (Point) o; //对o进行拆装,将字段从已装箱的实例复制到栈变量
}
在最后一行,C#编译器会生成一条IL指令对o进行拆箱,并生成另一条IL指令将这些字段从堆复制到基于栈的变量p中。
再看看一下代码:
public static void Main() {
Point p = new Point(); // 栈变量
p.x = p.y = ;
object o = p; // 对p进行装箱,o引用已装箱的实例
// 将Point的x字段变成2
p = (Point) o; // 对o进行拆装,将字段从已装箱的实例复制到栈变量
p.x = ; // 更改变量的状态
o = p; // 对p进行装箱,o引用已装箱的实例
}
最后三行代码唯一的目的就是将Point的x字段从1变成2.为此,首先要执行一次拆箱,在执行一次字段复制,在更改字段(在栈上),最后执行一次装箱(从而在托管堆上创建一个全新的已装箱实例)。希望你能体会到装箱和拆箱/复制操作对应用程序性能的影响。
在看个演示装箱和拆箱的例子:
private static void Main(string[] args)
{
Int32 v = ; // 创建一个伪装箱的值类型变量
Object o = v; // o引用一个已装箱的、包含值5的Int32
v = ; // 将未装箱的值修改成为123
Console.WriteLine(v + "," + (Int32)o); //显示"123,5"
}
你可以看出上述代码进行了几次装箱操作?如果说是3次,你会不会意味呢?我们来看下生成的IL代码。
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//代码大小
.maxstack
.locals init (
[] int32 num,
[] object obj2)
L_0000: nop // 将5加载到v中
L_0001: ldc.i4.
L_0002: stloc. // 对v进行装箱,将引用指针存储到o中
L_0003: ldloc.
L_0004: box int32
L_0009: stloc. // 将123加载到v中
L_000a: ldc.i4.s 0x7b
L_000c: stloc. // 对v进行装箱,并将指针保留在栈上以进行Concat(连接)操作
L_000d: ldloc.
L_000e: box int32 // 将字符串加载到栈上以执行Concat操作
L_0013: ldstr "," // 对o进行拆箱:获取一个指针,它指向栈上的Int32的字段
L_0018: ldloc.
L_0019: unbox.any int32 // 对Int32进行装箱,并将指针保留在栈上以进行Concat(连接)操作
L_001e: box int32 // 调用Concat
L_0023: call string [mscorlib]System.String::Concat(object, object, object)
// 将从Concat放回的字符串传给WriteLine
L_0028: call void
[mscorlib]System.Console::WriteLine(string)
L_002d: nop // 从Main返回
L_002e: ret
}
提示:主要原因是在Console.WriteLine方法上。
Console.WriteLine方法要求获取一个String对象,为了创建一个String对象,C#编译器生成的代码来调用String对象的静态方法Concate。该方法有几个重载的版本,唯一区别就是参数数量,在本例中需要连接三个数据项来创建一个字符串,所以编译器会选择以下Concat方法来调用:
public static String Concat(Objetc arg0, Object arg1, Onject arg2);
所以,如果像下面写对WriteLine的调用,生成的IL代码将具有更高的执行效率:
Console.WriteLine(v + "," + o); //显示"123,5"
这只是移除了变量o之前的(Int32)强制转换。就避免了一次拆箱和一次装箱。
我们还可以这样调用WriteLine,进一步提升上述代码的性能:
Console.WriteLine(v.ToString() + "," + o); //显示"123,5"
现在,会为未装箱的值类型实例v调用ToString方法,它返回一个String。String类型已经是引用类型,所以能直接传给Concat方法,不需要任何装箱操作。
下面在演示一个装箱和拆箱操作:
private static void Main(string[] args)
{
Int32 v = ; // 创建一个伪装箱的值类型变量
Object o = v; // o引用一个已装箱的、包含值5的Int32
v = ; // 将未装箱的值修改成为123
Console.WriteLine(v) //显示"123"
v = (Int32) o; //拆箱并将o复制到v
Console.WriteLine(v); //显示"5"
}
上述代码发生了多少次装箱呢?答案是一次。因为System.Console类定义了获取一个Int32作为参数的WriteLine方法的重载版本:
public static String Concat(Int32 value);
在WriteLine方法内部也许会发生装箱操作,但这已经不是我们能控制的。我们已经尽可能地从自己的代码中消除了装箱操作。
最后,如果知道自己写的代码会造成编译器反复对一个值类型进行装箱,请改用手动方式对值类型进行装箱。
对象相等性和同一性。System.Object类型提供了一个名为Equals的虚方法,它的作用是在两个对象包含相同的值得前提下返回true。如:
public class Object{
publick virtual Boolean Equals(Object obj) {
//如果两个引用指向同一个对象,它们肯定包含相同的值
if ( this == obj ) return true;
//假定对象不包含相同的值
return false;
}
}
对于Object的Equals方法的默认实现来说,它实现的实际是同一性,而非相等性。
public class Object{
public virtual Boolean Equals(Object obj) {
//要比较的对象不能为null
if (obj == null ) return false;
//如果对象类型不同,则肯定不相等
if (this.GetType() != obj.GetType()) return false;
//如果对象属于相同的类型,那么在它们所有字段都匹配的前提下返回true
//由于System.Object没有定义任何字段,所以字段是匹配的
return true;
}
}
由于,一个类型能重写Object的Equals方法,所以不能再调用这个Equals方法来测试同一性。为了修正这一问题,Object提供了一个静态方法ReferenEquals,其原型如下:
public class Object{
public static Boolean ReferenceEquals(Object objA , Object objB) {
retuen ( onjA == objB );
}
}
如果想检查同一性,务必调用ReferenceEquals,而不应该使用C#的== 操作符,因为==操作符可能被重载。
[CLR via C#]5.3 值类型的装箱和拆箱的更多相关文章
- CLR via 笔记 5.3 值类型的装箱和拆箱
1.装箱 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(Boxing)的机制. 1.在托管堆中分配好内存.分配的内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类 ...
- 【深入理解CLR】2:细谈值类型的装箱和拆箱
装箱 总所周知,值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用.但在许多情况下,都需要获取对值类型的一个实例的引用.例如,假定要创建一个A ...
- 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...
- [CLR via C#]值类型的装箱和拆箱
我们先来看一个示例代码: namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Array ...
- 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_值类型的装箱和拆箱(二)
[注意]:如果知道自己写的代码会造成编译器反复对一个值类型进行装箱,请改成用手动方式对值类型进行装箱. [好处]:代码会变得更小.更快. [例子]: using System; public seal ...
- [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱
在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...
- 浅谈.NET中的类型和装箱、拆箱原理
谈到装箱拆箱,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么建议你花点时间看看楼主这篇文章 1. .NET中的类型 为了 ...
- .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱
.NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
随机推荐
- VS2010编写动态链接库DLL和单元测试,转让DLL测试的正确性
本文将创建一个简单的动态库-link,谱写控制台应用程序使用该动态链接库,该动态链接库为"JAVA调用动态链接库DLL之JNative学习"中使用的DLL,仅仅是项目及文件名不同. ...
- openstack临时存储后端
声明: 本博客欢迎转发.但请保留原作者信息! 博客地址:http://blog.csdn.net/halcyonbaby 内容系本人学习.研究和总结,如有雷同,实属荣幸! 眼下openstack提供了 ...
- BigPipe设计原理
高性能页面加载技术--BigPipe设计原理及Java简单实现 1.技术背景 动态web网站的历史可以追溯到万维网初期,相比于静态网站,动态网站提供了强大的可交互功能.经过几十年的发展,动态网站在互动 ...
- python 性能- and-or 学习技能
C语言类似表情: bool ? a : b ,当表达式值为真的话,值为a.否则为b. 看一个样例: >>> a = "first" >>> b ...
- SQL Server 2008性能故障排查(四)——TempDB
原文:SQL Server 2008性能故障排查(四)--TempDB 接着上一章:I/O TempDB: TempDB是一个全局数据库,存储内部和用户对象还有零食表.对象.在SQLServer操作过 ...
- Java使用串行编程操作继电器
首先,我们必须建立一个良好的环境,那是,jdk并且tomcat.如果它不必须是web装了! 还有就是配置,也就是默认的comm.jar ,javax.comm.properties , win32co ...
- Data URI(转)
Data URL 早在 1995 年就被提出,那个时候有很多个版本的 Data URL Schema 定义陆续出现在 VRML 之中,随后不久,其中的一个版本被提上了议案——将它做个一个嵌入式的资源放 ...
- Duanxx的STM32学习:STM32F103中等容量的功能和外设
版权声明:本文博客原创文章,博客,未经同意,不得转载.
- [SignalR]注册路由
原文:[SignalR]注册路由 1.注册SignalR路由 在Asp.Net中,若是SignalR 1.*版本,在Global.asax文件中定义如下: 在Asp.Net中,若是SignalR 2. ...
- cocos2dx怎样设置ios和Android横屏竖屏的几种方法
cocos2d-x编译到ios上.默认是横屏的,若要改为http://竖屏.不同的ios版本号.方法也会不同 在ios7上或许我们设置好了横竖屏.但到了ios6上或许会变化.以下白白给大家分享一下我的 ...