CLR via 笔记 5.3 值类型的装箱和拆箱
1.装箱
为了将一个值类型转换成一个引用类型,要使用一个名为装箱(Boxing)的机制。
1.在托管堆中分配好内存。分配的内存量是值类型的各个字段需要的内存量加上托管堆的所有对象都有的两个额外成员(类型对象指针和同步块索引)需要的内存量。
2.值类型的字段复制到新分配的堆内存。
3.返回对象的地址。现在,这个地址是对一个对象的引用,值类型现在是一个引用类型。
2.拆箱
包含在已装箱对象中的所有字段都必须复制到值类型变量中,后者在线程栈上。CLR分两步完成这个复制操作。
第一步是获取已装箱的对象中的各个字段的地址。这个过程称为拆箱(Unboxing)。
第二步是将这些字段包含的值从堆中复制到基于栈的值类型实例中。
拆箱不是直接将装箱过程倒过来。拆箱的代价比装箱低得多。拆箱其实就是获取一个指针的过程,该指针指向包含在一个对象中的原始值类型(数据字段)。事实上,指针指向的是已装箱实例中的未装箱部分。所以,和装箱不同,拆箱不要求在内存中复制任何字节。知道这个重要的区别之后,还应知道的一个重点在于,问问会紧接着拆箱操作发生一次字段的复制操作。
显然,装箱和拆箱/复制操作会对应用程序的速度和内存消耗产生不利影响,所以应该注意编译器在什么时候生产代码来自动这些操作,并尝试手动编写代码,尽量避免自动生成代码的情况。
疑问:
大家如果装Resharper,每当我们把一个值类型和一个字符串拼接的话,他都会提示我们,无需把值类型ToString,如下图:
Resharper推荐使用第一种方法,那到底会不会有问题呢?
举个例子:
public static void Main()
{
Int32 v = ; //创建一个未装箱的值类型变量
Object o = v; //o引用一个已装箱的、包含值5的Int32
v = ; //将未装箱的值修改成123 Console.WriteLine(v + ", " + (Int32) o); //显示"123, 5"
}
大家猜猜这段代码发生了多少次装箱操作?如果说是3次,会不会觉得意外?
让我们仔细分析一下代码,理解具体发生的事情。
第一次装箱操作是第四行,把未装箱值类型实例(v)复给引用类型。
第二次和第三次其实就是字符串的拼接,WriteLine要求获取一个String对象,所以就必须采取某种方式对这些数据项进行合并,以创建一个String。
为了创建一个String,C#编译器生成代码来调用String对象的静态方法Concat。该方法有几个重载的版本,所有版本执行的操作都是一样的,唯一区别是参数数量,此次编译器选择的是Concat方法的下面这个版本:
public static String Concat(Object arg0, Object arg1, Object arg2);
也就是说第二次装箱是将未装箱值类型v变成Object类型,第三次是o强转成Int32,拆箱后再次装箱,并将新的已装箱实例的内存地址传递给Concat的arg2参数。Concat方法调用指定的每个对象的ToString方法, 并连接每个对象的字符串表示。从Concat返回的String对象随即传给WriteLine方法,以显示最终的结果。
应该指出的是,如果想下面这样写对WriteLine的调用,生成的IL代码将具有更高的执行效率:
Console.WriteLine(v + ", " + o); //显示“123, 5”
这和前面的版本几乎完全一致,只是移除了变量o之前的(Int32)强制转型,避免了一次拆箱和一次装箱,减少了额外对象的产生和相应的垃圾回收,将具有更高的效率。
解答:
通过ILdasm工具,我们来看一下,到底发生了什么:
这下大家明白了吧,它在IL处理的时候,还是偷偷进行装箱,所以事实证明,这个ToString方法不能省!所以有些时候也不能完全按照第三方插件的提示。
推荐的方法:
Console.WriteLine(v.ToString() + ", " + o); //显示“123, 5”
现在会为未装箱的值类型实例v调用ToString方法,它返回一个String。String对象已经是引用类型,所以能直接传给Concat方法,不需要任何装箱操作。
PS:
当然Console.WriteLine方法默认有多个针对值类型的重载,目的是为了减少装箱的次数。
但是这些方法不可能为所有种情况都考虑到,就如自定义的值类型或者如上面的例子等。建议大家在自己写代码是手动进行值类型的装箱操作,显示指明类型,避免CLR生成低效率的IL代码。
CLR via 笔记 5.3 值类型的装箱和拆箱的更多相关文章
- [CLR via C#]5.3 值类型的装箱和拆箱
原文:[CLR via C#]5.3 值类型的装箱和拆箱 在CLR中为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制. 下面总结了对值类型的一个实例进行装箱操作时内部发生的事: 1)在托管 ...
- 【深入理解CLR】2:细谈值类型的装箱和拆箱
装箱 总所周知,值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用.但在许多情况下,都需要获取对值类型的一个实例的引用.例如,假定要创建一个A ...
- 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱
目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...
- 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_值类型的装箱和拆箱(二)
[注意]:如果知道自己写的代码会造成编译器反复对一个值类型进行装箱,请改成用手动方式对值类型进行装箱. [好处]:代码会变得更小.更快. [例子]: using System; public seal ...
- [CLR via C#]值类型的装箱和拆箱
我们先来看一个示例代码: namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Array ...
- [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱
在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...
- 浅谈.NET中的类型和装箱、拆箱原理
谈到装箱拆箱,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么建议你花点时间看看楼主这篇文章 1. .NET中的类型 为了 ...
- [ 转载 ]学习笔记-深入剖析Java中的装箱和拆箱
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
- .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱
.NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
随机推荐
- js基本知识1
Javascript 作用 1. 网页特效 2. 用户交互 3. 表单验证 Js 就是可以用来控制 结构 和 样式 . 1.2 体验js 认识常用的三个输出语句. 都属于 js 内置对象 . 大家买手 ...
- 跟着百度学PHP[12]-文件处理 文件 目录
00x1 文件的属性 文件属性 <?php //-------------------------定义大小转换函数--------------- function changesize_dw($ ...
- HTML5 + AJAX ( jQuery版本 ) 文件上传带进度条
页面技术:HTML5 + AJAX ( jQuery) 后台技术:Servlet 3.0 服务器:Tomcat 7.0 jQuery版本:1.9.1 Servlet 3.0 代码 package or ...
- 关于js中__proto__和prototype的一些理解<转>
var Person = function(name) { this.name = name; } var p = new Person(); new操作符的操作是 var p = {} p. ...
- BlueZ--内核层+应用层
BlueZ 1.Kernel层实现: bluetooth协议栈有多层结构,最底层的硬件协议在硬件中就已经实现了.软件级别的协议实现,从HCI这一层开始实现. BlueZ对各层协议的实现是依托于Sock ...
- Hibernate Annotation 字段 默认值
http://emavaj.blog.163.com/blog/static/133280557201032262741999/ ——————————————————————————————————— ...
- DP - 字符混编
字符混编 Problem's Link ---------------------------------------------------------------------------- Mea ...
- Linux - Ubuntu开启SSH服务
SSH分客户端openssh-client和openssh-server. 如果你只是想登陆别的机器的SSH只需要安装openssh-client(ubuntu有默认安装,如果没有则sudo apt- ...
- 数据库 数据库SQL语句四
多表查询 等值连接 --查询员工信息,员工号,姓名,月薪,部门名称 select e.empno,e.ename,d.dname from emp e,dept d where e.deptno=d. ...
- Bitcoin: A Peer-to-Peer Electronic Cash System(比特币论文翻译)
比特币历史: 2008年,比特币论文诞生 2009年1月,第一批比特币诞生 2011年4月,比特币价格第一次达到了1美元 2011年6月,涨到30美元,然后开始跌 2013年1月,4美元 2013年1 ...