C#中值类型和引用类型的最终基类都是Object类型(它本身是一个引用类型)。利用装箱和拆箱功能,可通过允许值类型的任何值和Object类型的值相互转换。
装箱(boxing):就是把值类型转换为引用类型时发生
拆箱(unboxing):就是把引用类型转换为值类型时发生(被装过箱的的对象才能被拆箱)
.NET中值类型之会在栈中分配。引用类型分配内存与托管堆。
1.装箱
装箱操作
对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。
1.首先从托管堆中为新生成的引用对象分配内存
2.然后将值类型的数据拷贝到刚刚分配的内存中
3.返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用
说明进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
2.拆箱
拆箱操作
1.首先获取托管堆中属于值类型那部分字段的地址,(这步是严格意义上的拆箱)
2.将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
上代码
int num1=10; object numObj=num1; int num2=(int)numObj;
1.分析
首先这三句代码中出现了三个变量,两个类型
int类型:num1,num2 (int本质上是一个结构体(struct)所有int是一个值类型)
object类型:numObj (object本质是一个类(Class)类属于引用类型)
由此我们可以得出
int——>Object 装箱
object numObj=num;
Object——>int 拆箱
int num2=(int)numObj;
1.引用类型之间不属于装箱,拆箱。装箱,拆箱必须是值类型—>引用类型或者引用类型—>值类型
Person p=new Student(); // 这个叫隐式类型转换,不叫装箱 Student stu=(Student)p; //这叫显示类型转换,不叫拆箱
2.方法重载时,如果具有该类型的重载,那么就不叫装箱与拆箱
int n=10; Console.WriteLine(n);//没有发生装箱,因为方法重载
3.接口与值类型之间的装箱与拆箱
int n=2; IComparable c=n; int m=(int)c; Console.WriteLine(m.ToString());
调用含Object类型的方法的时候,传入值类型的时候就会发生装箱,方法之中处理该参数的时候会发生拆箱。定义泛型的时候,如果未来通过定义Object类型,当添加值类型的时候就会发生装箱与拆箱。
将一个类型转换为另一个类型,的过程中始终都是有时间损耗的,所以不管是java还是C#都引入了泛型的概念,来避免拆箱装箱所带来的时间损耗问题,具体如下代码
1 namespace 2 3 { 4 5 public class Test{ 6 7 public void Test001(){ 8 9 ArrayList arrayList = new ArrayList(); 10 Stopwatch s = new Stopwatch(); 11 12 s.Start();//--开始计时 13 for (int i = 0; i < 40000000; i++) 14 { 15 arrayList.Add(i);//这里发生了装箱的操作,将int装载成object(进行了40000000次的装箱) 16 } 17 s.Stop();--结束计时 18 Console.WriteLine("装箱耗费时间:" + s.Elapsed); 19 20 } //func 21 22 public void Test002(){ 23 24 List<int> list = new List<int>(); 25 26 Stopwatch s = new Stopwatch(); 27 28 s.Start();//-开始计时 29 30 for (int i = 0; i < 40000000; i++) 31 { 32 list.Add(i);// list 默认只存放int型,所以没有装拆操作(进行了40000000次的装箱) 33 } 34 35 s.Stop();--结束计时 36 Console.WriteLine("不装箱耗费时间:" + s.Elapsed); 37 38 } 39 40 }//class 41 42 class Program{ 43 44 static void main(string[] args){ 45 46 Test t = new Test(); 47 48 //调用两个方法分别测试拆装箱和不拆装箱执行同样的任务所耗的时间 49 50 t.Test001(); 51 52 t.Test002(); 53 54 } 55 56 } 57 58 }//namespace
测试结果如图:
因此,在数据量大,或者频繁操作 的情况下最好还是避免装箱和拆箱操作,从时间上来看,装箱耗费的时间资源是非常大的
1.警惕隐式类型转换--使用合理的方法进行类型转换
一些情况下我们很容易忽略了一些值类型隐式换为System.Object的操作
列如:
string s="测试"; s=s+1;
1是值类型,s是引用类型,这个”s +1”操作,虽然没有显式的类型转换,但是确实发生了装箱的操作
所以我们应该如下操作
string s="测试"; s=s+1.ToString();
这个调用了Int32的ToString()方法,就变为两个string类型的数据添加,就不存在装箱操作了
2.使用泛型--运行时绑定数据类型,减少拆箱与装箱
1、拆箱和装箱的存在,让值类型和引用类型之间的转换变得方便
2、但是在大量的数据操作中,频繁的装箱和拆箱操作会大大消耗CPU的资源,降低代码的执行速率
3、为了解决这个问题,我们要合理的使用类型转换和泛型类与泛型方法来防止隐式的装箱和拆箱操作
https://blog.csdn.net/m0_37852399/article/details/83786217
https://blog.csdn.net/qq_32452623/article/details/53910726
https://blog.csdn.net/yulongguiziyao/article/details/89044345
原文:https://www.cnblogs.com/zhoupy/p/12016678.html