个人理解(本质):

封箱是把值类型转换为引用类型

拆箱是把引用类型转换为值类型

封箱是把值类型转换为System.Object类型,或者转换为由值类型实现的接口类型:

例如:

struct Mystruct

{

public int a;

}

这是一个值类型, 装箱:

Mystruct tmp = new Mystruct();

tmp.a = 5;

object newTmp = tmp;

拆箱:

Mystruct bb = (Mystruct)newTmp;

封箱的作用就是可以提供一个备份

装箱(boxing)和拆箱(unboxing)是C#类型系统的核心概念.是不同于C与C++的新概念!,通过装箱和拆箱操作,能够在值类型和引用类型中架起一做桥梁.换言之,可以轻松的实现值类型与引用类型的互相转换,装箱和拆箱能够统一考察系统,任何类型的值最终都可以按照对象进行处理.

装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作。

1. 装箱在值类型向引用类型转换时发生

2. 拆箱在引用类型向值类型转换时发生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//1、
//   装箱和拆箱是一个抽象的概念
//2、
//   装箱是将值类型转换为引用类型 ;拆箱是将引用类型转换为值类型 
//   利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将//值类型与引用类型链接起来
//例如:
int val = 100;
object obj = val;
Console.WriteLine (“对象的值 = {0}", obj);
//这是一个装箱的过程,是将值类型转换为引用类型的过程
int val = 100;
object obj = val;
int num = (int) obj;
Console.WriteLine ("num: {0}", num);
//这是一个拆箱的过程,是将值类型转换为引用类型,再由引用类型转换为值类型的过程
//注:被装过箱的对象才能被拆箱
//3、
 //   .NET中,数据类型划分为值类型和引用(不等同于C++的指针)类型,与此对应,内//存分配被分成了两种方式,一为栈,二为堆,注意:是托管堆。
 //  值类型只会在栈中分配。
  //  引用类型分配内存与托管堆。
  // 托管堆对应于垃圾回收。
//4:装箱/拆箱是什么?
//装箱:用于在垃圾回收堆中存储值类型。装箱是值类型到 object 类型或到此值类型所实//现的任何接口类型的隐式转换。
//拆箱:从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换。
//5:为何需要装箱?(为何要将值类型转为引用类型?)
//一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为//型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
//另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为//Object。于是,要将值类型数据加入容器时,需要装箱。
//6:装箱/拆箱的内部操作。
//装箱:
//对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。
//第一步:新分配托管堆内存(大小为值类型实例大小加上一个方法表指针和一个//SyncBlockIndex)。
//第二步:将值类型的实例字段拷贝到新分配的内存中。
//第三步:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。
//有人这样理解:如果将Int32装箱,返回的地址,指向的就是一个Int32。我认为也不是//不能这样理解,但这确实又有问题,一来它不全面,二来指向Int32并没说出它的实质//(在托管堆中)。
//拆箱:
//检查对象实例,确保它是给定值类型的一个装箱值。将该值从实例复制到值类型变量中。
//有书上讲,拆箱只是获取引用对象中指向值类型部分的指针,而内容拷贝则是赋值语句之//触发。我觉得这并不要紧。最关键的是检查对象实例的本质,拆箱和装箱的类型必需匹 //配,这一点上,在IL层上,看不出原理何在,我的猜测,或许是调用了类似GetType之//类的方法来取出类型进行匹配(因为需要严格匹配)。
//7:装箱/拆箱对执行效率的影响
//显然,从原理上可以看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是//造成效率降低。
//那该如何做呢?
//首先,应该尽量避免装箱。
//比如上例2的两种情况,都可以避免,在第一种情况下,可以通过重载函数来避免。第二//种情况,则可以通过泛型来避免。
//当然,凡事并不能绝对,假设你想改造的代码为第三方程序集,你无法更改,那你只能是//装箱了。
//对于装箱/拆箱代码的优化,由于C#中对装箱和拆箱都是隐式的,所以,根本的方法是对//代码进行分析,而分析最直接的方式是了解原理结何查看反编译的IL代码。比如:在循环//体中可能存在多余的装箱,你可以简单采用提前装箱方式进行优化。
//8:对装箱/拆箱更进一步的了解
//装箱/拆箱并不如上面所讲那么简单明了,比如:装箱时,变为引用对象,会多出一个方//法表指针,这会有何用处呢?
//我们可以通过示例来进一步探讨。
//举个例子。
Struct A : ICloneable
{
public Int32 x;
public override String ToString() {
return String.Format(”{0}”,x);
}
public object Clone() {
return MemberwiseClone();
}
}
static void main()
{
A a;
a.x = 100;
Console.WriteLine(a.ToString());
Console.WriteLine(a.GetType());
A a2 = (A)a.Clone();
ICloneable c = a2;
Ojbect o = c.Clone();
}
//5.0:a.ToString()。编译器发现A重写了ToString方法,会直接调用ToString的指//令。因为A是值类型,编译器不会出现多态行为。因此,直接调用,不装箱。//(注:ToString是A的基类System.ValueType的方法)
//5.1:a.GetType(),GetType是继承于System.ValueType的方法,要调用它,//需要一个方法表指针,于是a将被装箱,从而生成方法表指针,调用基类的//System.ValueType。(补一句,所有的值类型都是继承于System.ValueType  //的)。
//5.2:a.Clone(),因为A实现了Clone方法,所以无需装箱。
//5.3:ICloneable转型:当a2为转为接口类型时,必须装箱,因为接口是一种引用类 //型。
//5.4:c.Clone()。无需装箱,在托管堆中对上一步已装箱的对象进行调用。
//附:其实上面的基于一个根本的原理,因为未装箱的值类型没有方法表指针,所以,不能//通过值类型来调用其上继承的虚方法。另外,接口类型是一个引用类型。对此,我的理 //解,该方法表指针类似C++的虚函数表指针,它是用来实现引用对象的多态机制的重要//依据。
//9:如何更改已装箱的对象
//对于已装箱的对象,因为无法直接调用其指定方法,所以必须先拆箱,再调用方法,但再//次拆箱,会生成新的栈实例,而无法修改装箱对象。有点晕吧,感觉在说绕口令。还是举//个例子来说:(在上例中追加change方法)
public void Change(Int32 x) {
this.x = x;
}
//调用:
A a = new A();
a.x = 100;
Object o = a; //装箱成o,下面,想改变o的值。
((A)o).Change(200); //改掉了吗?没改掉。
//没改掉的原因是o在拆箱时,生成的是临时的栈实例A,所以,改动是基于临时A的,并未//改到装箱对象。
//(附:在托管C++中,允许直接取加拆箱时第一步得到的实例引用,而直接更改,但C#//不行。)
//那该如何是好?
//嗯,通过接口方式,可以达到相同的效果。
//实现如下:
interface IChange {
void Change(Int32 x);
}
struct A : IChange {
}
//调用:
((IChange)o).Change(200);//改掉了吗?改掉了。
//为啥现在可以改?
//在将o转型为IChange时,这里不会进行再次装箱,当然更不会拆箱,因为o已经是引用//类型,再因为它是IChange类型,所以可以直接调用Change,于是,更改的也就是已//装箱对象中的字段了,达到期望的效果。
//10、--------------------------
 //   将值类型转换为引用类型,需要进行装箱操作(boxing):
//1、首先从托管堆中为新生成的引用对象分配内存。
//2、然后将值类型的数据拷贝到刚刚分配的内存中。
//3、返回托管堆中新分配对象的地址。
//可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
//将引用内型转换为值内型,需要进行拆箱操作(unboxing):
//1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。
//2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
//经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但//伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
//11、-------------------------
//NET的所有类型都是由基类System.Object继承过来的,包括最常用的基础类型:int, //byte, short,bool等等,就是说所有的事物都是对象。如果申明这些类型得时候都在//堆(HEAP)中分配内存,会造成极低的效率!(个中原因以及关于堆和栈得区别会在另一篇//里单独得说说!)
//.NET如何解决这个问题得了?正是通过将类型分成值型(value)和引用型//(regerencetype),C#中定义的值类型包括原类型(Sbyte、Byte、Short、//Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、//Decimal)、枚举(enum)、结构(struct),引用类型包括:类、数组、接口、委托、//字符串等。
//值型就是在栈中分配内存,在申明的同时就初始化,以确保数据不为NULL;
//引用型是在堆中分配内存,初始化为null,引用型是需要GARBAGE COLLECTION来//回收内存的,值型不用,超出了作用范围,系统就会自动释放!
//下面就来说装箱和拆箱的定义!
//装箱就是隐式的将一个值型转换为引用型对象。比如:
int i=0;
Syste.Object obj=i;
//这个过程就是装箱!就是将i装箱!
//拆箱就是将一个引用型对象转换成任意值型!比如:
int i=0;
System.Object obj=i;
int j=(int)obj;
//这个过程前2句是将i装箱,后一句是将obj拆箱!
 
 

拆箱操作的执行过程和装箱操作过程正好相反,是将存储在堆上的引用类型值转换为值类型并给值类型变量。

装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型来减少装箱操作和拆箱操作消耗。

深入理解C#的装箱和拆箱的更多相关文章

  1. 深入理解C#的装箱和拆箱(转)

    装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作.  1. 装箱在值类型向引用类型转换时发生 2. 拆箱在引用类型向值类型转换时发生 光上述两句话不难理解,但是往深处了解,就需要一些篇幅来解释了 ...

  2. 全面理解java自动装箱和拆箱(转)

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  3. 【.Net】深入理解C#的装箱和拆箱

    装箱和拆箱是值类型和引用类型之间相互转换是要执行的操作.  1. 装箱在值类型向引用类型转换时发生 2. 拆箱在引用类型向值类型转换时发生 光上述两句话不难理解,但是往深处了解,就需要一些篇幅来解释了 ...

  4. 深入理解Java之装箱与拆箱

    一.Java数据类型 1.在说装箱与拆箱之前,先说一下Java的基本数据类型,Java从数据类型上可以划分为值类型与引用类型,值类型是四类八种,分别是: 整数型:byte̵,short̵,int̵,l ...

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

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

  6. Java基础 带你深刻理解自动装箱,拆箱含义

    1.什么是装箱,什么是拆箱装箱:把基本数据类型转换为包装类.拆箱:把包装类转换为基本数据类型.基本数据类型所对应的包装类:int(几个字节4)- Integerbyte(1)- Byteshort(2 ...

  7. 如何理解Java中的自动拆箱和自动装箱?

    小伟刚毕业时面的第一家公司就被面试官给问住了... 如何理解Java中的自动拆箱和自动装箱? 自动拆箱?自动装箱?什么鬼,听都没听过啊,这...这..知识盲区... 回到家后小伟赶紧查资料,我透,这不 ...

  8. 关于C#理解装箱与拆箱

    目录 1.理解装箱 2.理解拆箱 3.生成的 IL 代码 4.实际应用 5.小结 1.理解装箱 简单地说,装箱就是将一个值类型的数据存储在一个引用类型的变量中. 假设你一个方法中创建了一个 int 类 ...

  9. 深入理解Java中的装箱与拆箱

    一.Java数据类型 1.在说装箱与拆箱之前,先说一下Java的基本数据类型,Java从数据类型上可以划分为值类型与引用类型,值类型是四类八种,分别是: 整数型:byte̵,short̵,int̵,l ...

随机推荐

  1. seajs教程(一):基本用法

    介绍 SeaJS 是一个适用于 Web 浏览器端的模块加载器.使用 SeaJS,可以更好地组织 JavaScript 代码. Sea.js 遵循 CMD 规范,模块化JS代码.依赖的自动加载.配置的简 ...

  2. Wow6432Node

    64 位版本 Windows 中的注册表分为 32 位注册表项和 64 位注册表项.许多 32 位注册表项与其相应的 64 位注册表项同名,反之亦然. 64 位版本 Windows 包含的默认 64 ...

  3. 【Linux】Linux中常用操作命令(转)

    Linux简介及Ubuntu安装 Linux,免费开源,多用户多任务系统.基于Linux有多个版本的衍生.RedHat.Ubuntu.Debian 安装VMware或VirtualBox虚拟机.具体安 ...

  4. intel汇编笔记

    另一篇汇编学习笔记AT&T Assembly on Linux  (linux下) mov ax,bx     bx到ax 读数据过程:cpu通过地址线发送地址a,控制线向存储器发送读命令,存 ...

  5. eclipse 插件编写(三)

    参考:http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2Fworkbench_ ...

  6. Sysinternals套件2016年11月更新发布,诸多工具被更新

    Sysinternals 实用程序可帮助您管理.解决和诊断 Windows 系统和应用程序,在 Sysinternals 的 2016 年 11 月更新中,微软增强 在 sysmon 对注册表和文件事 ...

  7. QML被系统缓存的原理是比较时间戳

    Gunnar Roth January 25, 2017 at 17:07 Afaik the cached qml file contains a checksum of the original ...

  8. Qt在Windows下如何创建无CMD窗口控制台程序

    默认情况下,用Qt新建一个控制台程序,运行时会弹出CMD窗口.如何把窗口去掉呢? *.pro文件默认是这样的: TEMPLATE = app CONFIG += console CONFIG -= a ...

  9. 线程天敌TerminateThread与SuspendThread

    线程天敌TerminateThread与SuspendThread 作者:童磊(magictong) 目的:不是演示TerminateThread和SuspendThread的原理而是希望能在自己的程 ...

  10. 注册表Demo

    一.获取安装程序信息 #include <windows.h> #include <iostream> #include <string> #include < ...