C#基础复习(2) 之 装箱拆箱
参考资料
[1] @只增笑耳Jason的回答 https://www.zhihu.com/question/57208269
[2] 《C# 捷径教程》
疑难解答
装箱和拆箱是什么?
在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# 捷径教程》所说,当以下任意转换发生时,值类型就被装箱。
- 从值类型转换成对对象引用
- 从值类型转换成System.ValueType引用
- 从值类型转换成指向值类型实现的接口的引用
- 从枚举类型转换成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()来调用方法,则不会发生装箱。
对于拆箱操作,其发生时机恰好与装箱操作相反,当以下任意转换发生时,引用类型就被拆箱为值类型。
- 从引用类型强制转回到值类型
- 从System.ValueType类型转换成值类型
- 从指向值类型实现的接口的引用类型转换成值类型
- 从System.Enum引用类型转换成值类型
装箱与拆箱的效率如何?
先说结论,装箱是低效的,对于拆箱,CLR的拆箱操作本身并不是低效的,低效的根源在于C#通常把拆箱操作和一个对值复制的操作组合在一起。
在C#中,大部分装箱操作都是隐式的,举个例子,当我们将一连串的整型(值类型)插入到ArrayList(非泛型版本)中去时,每插入一次,都是一次装箱操作。
那么如何避免装箱拆箱呢?一个有效的方法是使用泛型,C#中有武装到牙齿的泛型,对于上述向列表插入的值的方法,完全可以使用List来进行替代。
C#基础复习(2) 之 装箱拆箱的更多相关文章
- 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 ...
- C#装箱拆箱
. 装箱和拆箱是一个抽象的概念 2. 装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型 利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类 ...
- Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口
Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器 ...
- java自动装箱拆箱总结
对于java1.5引入的自动装箱拆箱,之前只是知道一点点,最近在看一篇博客时发现自己对自动装箱拆箱这个特性了解的太少了,所以今天研究了下这个特性.以下是结合测试代码进行的总结. 测试代码: int a ...
- CLR via C# 中关于装箱拆箱的摘录
装箱: 为了将一个值类型转换成一个引用类型,要使用一个名为装箱(boxing)的机制.下面总结了对值类型的一个实例进行装箱操作时在内部发生的事情. 1.在托管堆中分配好内存.分配的内存量是值类型的各 ...
- c#学习系列之装箱拆箱
1. 装箱和拆箱是一个抽象的概念 2. 装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型 利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的 ...
- Java中的自动装箱拆箱
Java中的自动装箱拆箱 一.自动装箱与自动拆箱 自动装箱就是将基本数据类型转换为包装类类型,自动拆箱就是将包装类类型转换为基本数据类型. 1 // 自动装箱 2 Integer total = 90 ...
- 通过源码了解Java的自动装箱拆箱
什么叫装箱 & 拆箱? 将int基本类型转换为Integer包装类型的过程叫做装箱,反之叫拆箱. 首先看一段代码 public static void main(String[] args) ...
- 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱
引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并 ...
随机推荐
- 【校招面试 之 C/C++】第3题 为什么要内存对齐?以及内存对齐的方式
1.为什么要进行内存对? 参考:https://blog.csdn.net/a369000753/article/details/51188915 所谓内存对齐,是为了让内存存取更有效率而采用的一种编 ...
- python的paramiko模块简单应用
用法1,SSHClient 分别可以使用密码和秘钥登陆,然后执行命令,并且获取执行结果 import paramiko #创建一个SSH对象 ssh = paramiko.SSHClient() #允 ...
- 关于weblogic报UnsatisfiedLinkError Native Library xxx.so already loaded
一.场景 最近写的一个系统,在Tomcat测试完后说要改使用weblogic,于是在服务器上安装了weblogic,捣鼓了半天,一个个问题冒了出来,其中就有个比较麻烦的报错:UnsatisfiedLi ...
- 两个应用之间传递广播的规则 Broadcast
sendBroadcast(new Intent(Config.ACTION_PRINT),”com.qf.permission.print”);先判断应用有没有对应的权限 再去判断有没有对应的act ...
- phpcms如何给已有的模块添加新功能?
phpcms如何给已有的模块添加新功能? 方法一:直接在模块里的控制器文件中添加功能. 不建议使用此方法,因为一旦phpcms升级,有可能会覆盖模块中的文件, 导致你添加的功能丢失. 方法二:新建一个 ...
- PAT 1053 住房空置率 (20)(代码+思路)
1053 住房空置率 (20)(20 分) 在不打扰居民的前提下,统计住房空置率的一种方法是根据每户用电量的连续变化规律进行判断.判断方法如下: 在观察期内,若存在超过一半的日子用电量低于某给定的阈值 ...
- web服务器部署过程记录
由于之前没有服务器部署经验,又选择了所有软件都是单独编译安装,遇到很多问题,解决之后还是学习到了很多新东西. 如今回过头来还是选择lnmp集成环境的部署方式比较方便快捷:https://lnmp.or ...
- Task构造
//原文:http://www.tuicool.com/articles/IveiQbQ 创建并且初始化Task 使用lambda表达式创建Task Task.Factory.StartNew(() ...
- oracle常用函数速记
1.截断中文字符串 CREATE OR REPLACE function cn_cutstr(v_str varchar2,v_len number) return varchar2 IS v_i n ...
- linux 操作系统rz sz 快速上传和下载文件
## Centos 安装 rz sz yum install lrzsz ### Ubuntu 安装 apt-get install lrzsz