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六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
随机推荐
- ThreadPoolExecutor线程池解析与BlockingQueue的三种实现
目的 主要介绍ThreadPoolExecutor的用法,和较浅显的认识,场景的使用方案等等,比较忙碌,如果有错误还请大家指出 ThreadPoolExecutor介绍 ThreadPoolExecu ...
- java学习之局部变量以及全局变量
全局变量 什么是全局变量? 全局变量就好比一个容器或者一个公用的东西一样,就类似外面公共场所的凳子一样,大家都可以使用这个凳子. 和他相反的局部变量是啥子东东呢? 局部变量就是局部的东西,如果全局变量 ...
- 使用ReaderWriterLock类实现多用户读/单用户写同步
使用ReaderWriterLock类实现多用户读/单用户写同步[1] 2015-03-12 应用程序在访问资源时是进行读操作,写操作相对较少.为解决这一问题,C#提供了System.Threadin ...
- python 将base64字符串还原成图片保存
import os,base64 strs='''/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCI ...
- 基于jQuery的图片异步加载和预加载实例
如今的网页中有很多图片,比如相册列表,那么如果一次性读取图片将会瞬间加重服务器的负担,所以我们用jQuery来实现图片的异步加载和预加载功能,这样在页面的可视范围内才会加载图片,当拖动页面至可视界面时 ...
- beaglebone black ubuntu display x11 server的配置
Change default resolution on BeagleBone modesetting vs fbdev digiteltlc May 7th, 2014, 03:28 PM Hi ...
- Mybatis Batch 批量操作
Mybatis Batch 批量操作 http://www.blogjava.net/diggbag/articles/mybatis.html
- Android——Android Bundle类(转)
今天发现自己连Bundle类都没有搞清楚,于是花时间研究了一下. 根据google官方的文档(http://developer.android.com/reference/android/os/Bun ...
- C#代码获取或设置Iframe中的HTML
在最近的数据采集研究中, 发现很多页面的内容都是在iframe中的, 这位采集带来了不少困难. 经过一番思考之后, 我想到了C#的解决办法: 1. 运行Spider Studio, 加载页面 http ...
- JVM调优浅谈(转)
1.数据类型 java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:它代表的值就是数值本身,而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本 ...