参考资料

[1] @只增笑耳Jason的回答 https://www.zhihu.com/question/57208269

[2] 《C# 捷径教程》

疑难解答

  1. 装箱和拆箱是什么?
  2. 何时发生装箱与拆箱?
  3. 装箱与拆箱的效率如何?

装箱和拆箱是什么?

在C#中,装箱和拆箱发生在值类型与引用类型之间。当我们把一个值类型转换成引用类型时,就发生了装箱操作。反之,当我们将一个引用类型转换成值类型时,就发生了拆箱操作。对于值类型和引用类型,感觉《C# 捷径教程》中讲的比较详尽(当然也可以参考我上一篇写的关于值类型与引用类型的文章):

你用类来定义对象,用结构来定义值。二者之间存在一个清晰的界限。对象存活在有垃圾回收的内存堆上。值通常存活在临时的存储空间里,比如栈。前面提到过的一个显著的例外就是,如果值类型作为一个字段被包含在一个对象中,那它就可以存活在堆上。它不是自治的,GC不直接控制它的生命周期。

对于装箱拆箱,知乎上有个答主感觉回答的很精辟:

简单地讲装箱就是把一个放在stack上的值移动到heap上,拆箱正好相反

如下代码展示了一个较为典型的装箱操作:

1 class BoxAndUnBox{
2
3 public static void Main(string[] args) {
4 int a = 1;
5
6 Print(a); // =>在此处值类型a被装箱为object类型
7 }
8
9 static void Print(object obj) {
10 Console.WriteLine(obj);
11 }
12 }

在第6行处发生了装箱操作,变量a为值类型int,给a分配的空间在本地栈上。而Print方法接受一个对象引用,这个对象引用是一个指向基于堆的对象的引用。

当我们将值类型传入该方法时,就发生了装箱操作,《C# 捷径教程》对此是这样描述的:

CLR创建了一个运行时包装器类来包含这个值类型的副本。包装器类的实例存活在对上,通常称为装箱对象。这是CLR来联系值类型和引用类型之间间隔的方法。

其中在第六行发生的操作如下图所示。

被装箱后,关键的点是箱子内的值是初始值的副本,这意味着我们就算对箱子内的值进行更改,也不会影响到初始值(但并不总是这样,如果使用接口类型进行装箱,则修改原始值是可能的)。

何时发生装箱与拆箱?

根据《C# 捷径教程》所说,当以下任意转换发生时,值类型就被装箱。

  1. 从值类型转换成对对象引用
  2. 从值类型转换成System.ValueType引用
  3. 从值类型转换成指向值类型实现的接口的引用
  4. 从枚举类型转换成System.Enum引用

第三种情况光看文字描述可能不太清楚,下面上代码。

1 public interface IPrint {
2 void Print();
3 }
4
5 public struct Value : IPrint {
6
7 public int x;
8
9 public Value(int x) {
10 this.x = x;
11 }
12
13 public void Print() {
14 Console.WriteLine(x);
15 }
16 }
17
18 class BoxAndUnBox{
19 public static void Main(string[] args) {
20 Value value = new Value(3);
21 IPrint print = value;
22 print.Print();
23 }
24 }

其中在第21行,值类型value被装箱为IPrint接口类型。这个过程是隐式的,事实上,如果我们直接通过value.Print()来调用方法,则不会发生装箱。

对于拆箱操作,其发生时机恰好与装箱操作相反,当以下任意转换发生时,引用类型就被拆箱为值类型。

  1. 从引用类型强制转回到值类型
  2. 从System.ValueType类型转换成值类型
  3. 从指向值类型实现的接口的引用类型转换成值类型
  4. 从System.Enum引用类型转换成值类型

装箱与拆箱的效率如何?

先说结论,装箱是低效的,对于拆箱,CLR的拆箱操作本身并不是低效的,低效的根源在于C#通常把拆箱操作和一个对值复制的操作组合在一起。

在C#中,大部分装箱操作都是隐式的,举个例子,当我们将一连串的整型(值类型)插入到ArrayList(非泛型版本)中去时,每插入一次,都是一次装箱操作。

那么如何避免装箱拆箱呢?一个有效的方法是使用泛型,C#中有武装到牙齿的泛型,对于上述向列表插入的值的方法,完全可以使用List来进行替代。

C#基础复习(2) 之 装箱拆箱的更多相关文章

  1. java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容

    8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...

  2. C#装箱拆箱

    .       装箱和拆箱是一个抽象的概念 2.       装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型        利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类 ...

  3. Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口

    Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...

  4. java自动装箱拆箱总结

    对于java1.5引入的自动装箱拆箱,之前只是知道一点点,最近在看一篇博客时发现自己对自动装箱拆箱这个特性了解的太少了,所以今天研究了下这个特性.以下是结合测试代码进行的总结. 测试代码: int a ...

  5. CLR via C# 中关于装箱拆箱的摘录

     装箱: 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(boxing)的机制.下面总结了对值类型的一个实例进行装箱操作时在内部发生的事情. 1.在托管堆中分配好内存.分配的内存量是值类型的各 ...

  6. c#学习系列之装箱拆箱

    1.      装箱和拆箱是一个抽象的概念 2.      装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型       利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的 ...

  7. Java中的自动装箱拆箱

    Java中的自动装箱拆箱 一.自动装箱与自动拆箱 自动装箱就是将基本数据类型转换为包装类类型,自动拆箱就是将包装类类型转换为基本数据类型. 1 // 自动装箱 2 Integer total = 90 ...

  8. 通过源码了解Java的自动装箱拆箱

    什么叫装箱 & 拆箱? 将int基本类型转换为Integer包装类型的过程叫做装箱,反之叫拆箱. 首先看一段代码 public static void main(String[] args) ...

  9. 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱

    引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...

随机推荐

  1. 常用特殊符号的HTML代码(HTML字符实体)

    适当使用实体,对页面开发有相当大的帮助. 自己收集的一些常用的以实体代替与HTML语法相同的字符,避免浏览解析错误. 常用HTML字符实体(建议使用实体): 字符 名称 实体名 实体数 • 圆点   ...

  2. How to Set Up an Rsync Daemon on Your Linux Server

    Introduction This tutorial will take you through setting up an rsync daemon on your Linux server. Yo ...

  3. 理解OAuth 2.0 (摘自阮一峰网络日志)

    OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为R ...

  4. 五个步骤搞定敏捷UX设计

    互联网产品发展的速度越来越快,人们对于产品的要求也在不断的升级,这直接地导致了用户体验设计的重要性不断提升.与此同时,过去的流程冗长的设计开发模式已经不能够满足快速迭代的需要.<敏捷宣言> ...

  5. css长度

    在CSS样式表中,长度单位分两种: 相对长度单位,如px, em等绝对长度单位,如pt,mm等 CSS相对长度单位(relative length unit) CSS相对长度单位中的相对二字,表明了其 ...

  6. 2018.10.15 bzoj4570: [Scoi2016]妖怪(凸包)

    传送门 不得不说这题有点东西啊. 看到题第一眼二分,用二次函数求范围来进行checkcheckcheck,20分滚粗了233. 于是开始思考正解. 发现可以把每只怪物的二元组属性看成二维坐标. 这时对 ...

  7. 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)

    传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...

  8. 2018.07.01 洛谷小B的询问(莫队)

    P2709 小B的询问 题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数 ...

  9. 2018.09.17 atcoder Digit Sum(数论)

    传送门 数论好题啊. 首先对于b<=sqrt(n)b<=sqrt(n)b<=sqrt(n)的情况直接枚举b判断一下就行了. 下面谈一谈如何解决b>sqrt(n)b>sqr ...

  10. 2018.08.30 bzoj4720: [Noip2016]换教室(期望dp)

    传送门 一道无脑的期望dp. 用f[i][j][0/1]表示前i堂课提出了j次申请且第i堂课没有(有)提出申请. 这样就可以状态转移了. 然而这题状态转移方程有点长... (主要是情况多... 代码: ...