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 值类型的装箱和拆箱的更多相关文章

  1. [CLR via C#]5.3 值类型的装箱和拆箱

    原文:[CLR via C#]5.3 值类型的装箱和拆箱 在CLR中为了将一个值类型转换成一个引用类型,要使用一个名为装箱的机制. 下面总结了对值类型的一个实例进行装箱操作时内部发生的事: 1)在托管 ...

  2. 【深入理解CLR】2:细谈值类型的装箱和拆箱

    装箱 总所周知,值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用.但在许多情况下,都需要获取对值类型的一个实例的引用.例如,假定要创建一个A ...

  3. 【.Net基础二】浅谈引用类型、值类型和装箱、拆箱

    目前在看CLR via C#,把总结的记下来,索性就把他写成一个系列吧. 1.[.Net基础一] 类型.对象.线程栈.托管堆运行时的相互关系 2.[.Net基础二]浅谈引用类型.值类型和装箱.拆箱 引 ...

  4. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_值类型的装箱和拆箱(二)

    [注意]:如果知道自己写的代码会造成编译器反复对一个值类型进行装箱,请改成用手动方式对值类型进行装箱. [好处]:代码会变得更小.更快. [例子]: using System; public seal ...

  5. [CLR via C#]值类型的装箱和拆箱

    我们先来看一个示例代码: namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Array ...

  6. [C#] 类型学习笔记一:CLR中的类型,装箱和拆箱

    在学习.NET的时候,因为一些疑问,让我打算把.NET的类型篇做一个总结.总结以三篇博文的形式呈现. 这篇博文,作为三篇博文的第一篇,主要探讨了.NET Framework中的基本类型,以及这些类型一 ...

  7. 浅谈.NET中的类型和装箱、拆箱原理

    谈到装箱拆箱,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么建议你花点时间看看楼主这篇文章 1. .NET中的类型 为了 ...

  8. [ 转载 ]学习笔记-深入剖析Java中的装箱和拆箱

    深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...

  9. .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱

    .NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...

随机推荐

  1. svn lock 锁定文件

    [root@NGINX-APACHE-SVN pro]# svn lock zh_CN.UTF-8 'zh_CN.UTF-8' locked by user 'svnroot'. [root@NGIN ...

  2. JAVA多线程之synchronized和volatile实例讲解

    在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...

  3. ognl概念和原理详解

    一.问题的提出   在mvc中,数据是在各个层次之间进行流转是一个不争的事实.而这种流转,也就会面临一些困境,这些困境,是由于数据在不同世界中的表现形式不同而造成的: 1. 数据在页面上是一个扁平的, ...

  4. 82. Single Number【easy】

    Given 2*n + 1 numbers, every numbers occurs twice except one, find it.   Example Given [1,2,2,1,3,4, ...

  5. jdom 插入 修改 删除

    创建XML文档 XML文件是一种典型的树形文件,每个文档元素都是一个document元素的子节点.而每个子元素都是一个Element对象,对象可以向下包含. 1 因此我们可以通过先创建元素再将元素添加 ...

  6. mof提权原理及其过程

    mof提权的原理: mof是windows系统的一个文件(在c:/windows/system32/wbem/mof/nullevt.mof)叫做"托管对象格式"其作用是每隔五秒就 ...

  7. SpringBoot DataSource 配置说明

    DataSource 配置说明 属性 说明 spring.dao.exceptiontranslation.enabled 是否开启PersistenceExceptionTranslationPos ...

  8. HashMap与ConcurrentHashMap的区别(转)

    从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是线程安全的,因此多线程操作时需要格外小心. 在JDK1.5中,伟大的Doug Lea给我们带来了concurrent包,从 ...

  9. _BV()

    #define _BV(bit) (1 << (bit)) _BV()是把1左移N位的函数._BV(7)相当于(1<<7) 常用于位的置位或清零 示例解析: PC7=7; PO ...

  10. javascript弹出层-DEMO001

    首先上一张图 看下弹出层的效果 从图中可以看到二部分 一是弹出层 二是遮照层 弹出层:即弹出你要操作的内容 遮照层:遮照住不要操作的内空 实际技术原理主要是 CSS +JS  (z-index是核心) ...