装拆箱

Posted 【我是谁】

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装拆箱相关的知识,希望对你有一定的参考价值。

装箱(boxing)和拆箱(unboxing)是C#类型系统的核心概念。

值类型继承至System.ValueType,引用类型继承至System.Object,详细的类继承关系如下图所示:

 

值类型实例通常分配在线程的栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据,而引用类型实例分配在托管堆(managed heap)上,变量保存了实例数据的内存引用.栈是一种先进后出,并且由系统自动操作的存储空间,而堆(在.NET上准确的说是托管堆Managed Heap)是一种自由储存区(Free Memory),在该区域中,必须明确的为对象申请存储空间(使用的new关键字):示例:
 public void Method() 

{ 

int x=3;  //第一行

int y=5;  //第二行

class cls1 = new class();  //第三行

}

下面逐行分析以上三行代码的执行过程。

1行:当这行代码执行时,编译器为它分配一小块栈内存,运行时栈负责提供程序所需的内存。

2行:栈在第一块内存的顶部分配了另一块内存。注意,内存的分配与释放遵循后进先出逻辑。

3行:编译器在栈上创建了一个指针,真实的对象存储在托管堆中。托管堆并不跟踪运行内存,它更像一堆随时可以访问的对象。托管堆上分配的内存大小由具体对象的大小而决定。

 

不扯了,说下装拆箱:

1、装箱就是将一个值类型转换成等价的引用类型。 它的过程分为这样几步:
1) 在托管堆上分配一个新的对象。
2) 将栈上的数据转移到托管堆新创建的对象空间上。
3) 在栈上分配一个指向托管堆的引用。
2、拆箱则是将一个已装箱的引用类型转换为值类型:
1)将托管堆上对象的数据转移到栈上
2) 垃圾回收器(GC)回收托管堆上的内存空间。
3)删除栈上分配的对象引用。
 
1             int n = 100;
2             //有没有发生装箱?没有,只是调用方法.查看IL没有box,unbox关键字
3             string s = Convert.ToString(n);
4             int m = int.Parse(s);
5             Console.ReadKey();
View Code

装拆箱:

1             int n = 100;
2             object o = n;//发生了一次装箱
3             int m = (int)o; //发生了一次拆箱
4             Console.WriteLine(m);
5             Console.ReadKey();
View Code

查看IL代码如下:

注意问题:装箱的时候是什么类型,拆箱的时候也必须使用对应的类型拆箱。否则会报错

=============================================================================================

之前写了一篇比较是否为同一对象(比较是否为同一对象),object.ReferenceEquals如果两个参数都是值类型会是怎样的呢?

 

1            int n = 100;
2             int m = 100;
3             Console.WriteLine(n.Equals(m));
4             Console.WriteLine(object.ReferenceEquals(n, m));
5             Console.ReadKey();
View Code

 

运行结果是false,我们来查看下IL代码,很明显,n和m都装箱变为了引用类型,它们所指向的地址不一样,无论如何都是不成立的

 

=================================================================================

拼接字符串也会涉及装箱问题:比如:

1             int n = 10;
2             object o = n; //装箱一次
3             n = 100;
4             Console.WriteLine(n + "," + (int)o); //装箱两次,拆箱一次
5             Console.ReadKey();
View Code

还是得查看一下IL代码:因为拼接字符串是调用string.Concat(object,object,object)这个方法,每拼接一个字符串会把int隐式装箱再连接

======================================================================

装箱和拆箱性能问题

使用ArrayList:

 1             ArrayList arrInt = new ArrayList();
 2             Stopwatch watch = new Stopwatch();
 3             watch.Start();
 4             for (int i = 0; i < 1000000; i++)
 5             {
 6                 arrInt.Add(i);
 7             }
 8             watch.Stop();
 9             Console.WriteLine(watch.Elapsed);
10             Console.ReadKey();
View Code

运行结果:

使用泛型:

 1     //使用泛型集合避免装箱和拆箱。
 2             List<int> arrInt = new List<int>();
 3             Stopwatch watch = new Stopwatch();
 4             watch.Start();
 5             for (int i = 0; i < 1000000; i++)
 6             {
 7                 arrInt.Add(i);
 8             }
 9             watch.Stop();
10             Console.WriteLine(watch.Elapsed);
11             Console.ReadKey();
View Code

运行结果:

这可以看出装拆箱还是比较影响性能的

 

以上是关于装拆箱的主要内容,如果未能解决你的问题,请参考以下文章

包装类自动装拆箱缓存数组

包装类自动装拆箱缓存数组

day001-日期格式类装拆箱

JAVA提高一:静态导入可变参数增强型for循环装拆箱

java基础 深入解析常量池与装拆箱机制

java基础 深入解析常量池与装拆箱机制