C# - 再谈C#的装箱和拆箱

转载 2016年08月31日 11:35:15

上一篇写了一下装箱拆箱的定义和IL分析,这一篇我们看下使用泛型和不使用泛型引发装箱拆箱的情况

1. 使用非泛型集合时引发的装箱和拆箱操作 

看下面的一段代码:

var array = new ArrayList();

array.Add(1);

array.Add(2);

foreach (int value in array){  

Console.WriteLine(“value is {0}”,value);

}

代码声明了一个ArrayList对象,向ArrayList中添加两个数字1,2;然后使用foreach将ArrayList中的元素打印到控制台。

在这个过程中会发生两次装箱操作和两次拆箱操作,在向ArrayList中添加int类型元素时会发生装箱,在使用foreach枚举ArrayList中的int类型元素时会发生拆箱操作,将object类型转换成int类型,在执行到Console.WriteLine时,还会执行两次的装箱操作;这一段代码执行了6次的装箱和拆箱操作;如果ArrayList的元素个数很多,执行装箱拆箱的操作会更多。

你可以通过使用ILSpy之类的工具查看IL代码的box,unbox指令查看装箱和拆箱的过程

2. 使用泛型集合的情况

请看如下代码:

var list = new List<int>();
list.Add(1);
list.Add(2);
 
foreach (int valuein list)
{
Console.WriteLine("value is {0}", value);
}

代码和1中的代码的差别在于集合的类型使用了泛型的List,而非ArrayList;我们同样可以通过查看IL代码查看装箱拆箱的情况,上述代码只会在Console.WriteLine()方法时执行2次装箱操作,不需要拆箱操作。

可以看出泛型可以避免装箱拆箱带来的不必要的性能消耗;当然泛型的好处不止于此,泛型还可以增加程序的可读性,使程序更容易被复用等等。

本文使用的C#代码如下:

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
using System;
using System.Collections;
using System.Collections.Generic;
 
namespace boxOrUnbox
{
    class Program
    {
        static void Main(string[] args)
        {
            //do nothing
        }
 
        static void Box()
        {
            object objValue = 9;
        }
 
        static void Unbox()
        {
            object objValue = 4;
            int value = (int)objValue;
        }
 
        static void LookatArrayList()
        {
            var array = new ArrayList();
            array.Add(1);
            array.Add(2);
 
            foreach (int value in array)
            {
                Console.WriteLine("value is {0}", value);
            }
        }
 
        static void LookatGenericList()
        {
            var list = new List<int>();
            list.Add(1);
            list.Add(2);
 
            foreach (int value in list)
            {
                Console.WriteLine("value is {0}", value);
            }
        }
    }
}

C#的IL代码如下:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
.class private auto ansi beforefieldinit boxOrUnbox.Program
    extends [mscorlib]System.Object
{
    // Methods
    .method private hidebysig static
        void Main (
            string[] args
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 2 (0x2)
        .maxstack 8
        .entrypoint
 
        IL_0000: nop
        IL_0001: ret
    // end of method Program::Main
 
    .method private hidebysig static
        void Box () cil managed 
    {
        // Method begins at RVA 0x2054
        // Code size 10 (0xa)
        .maxstack 1
        .locals init (
            [0] object objValue
        )
 
        IL_0000: nop
        IL_0001: ldc.i4.s 9
        IL_0003: box [mscorlib]System.Int32
        IL_0008: stloc.0
        IL_0009: ret
    // end of method Program::Box
 
    .method private hidebysig static
        void Unbox () cil managed 
    {
        // Method begins at RVA 0x206c
        // Code size 16 (0x10)
        .maxstack 1
        .locals init (
            [0] object objValue,
            [1] int32 'value'
        )
 
        IL_0000: nop
        IL_0001: ldc.i4.4
        IL_0002: box [mscorlib]System.Int32
        IL_0007: stloc.0
        IL_0008: ldloc.0
        IL_0009: unbox.any [mscorlib]System.Int32
        IL_000e: stloc.1
        IL_000f: ret
    // end of method Program::Unbox
 
    .method private hidebysig static
        void LookatArrayList () cil managed 
    {
        // Method begins at RVA 0x2088
        // Code size 114 (0x72)
        .maxstack 2
        .locals init (
            [0] class [mscorlib]System.Collections.ArrayList 'array',
            [1] int32 'value',
            [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
            [3] bool CS$4$0001,
            [4] class [mscorlib]System.IDisposable CS$0$0002
        )
 
        IL_0000: nop
        IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: ldc.i4.1
        IL_0009: box [mscorlib]System.Int32
        IL_000e: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
        IL_0013: pop
        IL_0014: ldloc.0
        IL_0015: ldc.i4.2
        IL_0016: box [mscorlib]System.Int32
        IL_001b: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
        IL_0020: pop
        IL_0021: nop
        IL_0022: ldloc.0
        IL_0023: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator()
        IL_0028: stloc.2
        .try
        {
            IL_0029: br.s IL_004a
            // loop start (head: IL_004a)
                IL_002b: ldloc.2
                IL_002c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
                IL_0031: unbox.any [mscorlib]System.Int32
                IL_0036: stloc.1
                IL_0037: nop
                IL_0038: ldstr "value is {0}"
                IL_003d: ldloc.1
                IL_003e: box [mscorlib]System.Int32
                IL_0043: call void [mscorlib]System.Console::WriteLine(stringobject)
                IL_0048: nop
                IL_0049: nop
 
                IL_004a: ldloc.2
                IL_004b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
                IL_0050: stloc.3
                IL_0051: ldloc.3
                IL_0052: brtrue.s IL_002b
            // end loop
 
            IL_0054: leave.s IL_0070
        // end .try
        finally
        {
            IL_0056: ldloc.2
            IL_0057: isinst [mscorlib]System.IDisposable
            IL_005c: stloc.s CS$0$0002
            IL_005e: ldloc.s CS$0$0002
            IL_0060: ldnull
            IL_0061: ceq
            IL_0063: stloc.3
            IL_0064: ldloc.3
            IL_0065: brtrue.s IL_006f
 
            IL_0067: ldloc.s CS$0$0002
            IL_0069: callvirt instance void [mscorlib]System.IDisposable::Dispose()
            IL_006e: nop
 
            IL_006f: endfinally
        // end handler
 
        IL_0070: nop
        IL_0071: ret
    // end of method Program::LookatArrayList
 
    .method private hidebysig static
        void LookatGenericList () cil managed 
    {
        // Method begins at RVA 0x2118
        // Code size 90 (0x5a)
        .maxstack 2
        .locals init (
            [0] class [mscorlib]System.Collections.Generic.List`1<int32> list,
            [1] int32 'value',
            [2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> CS$5$0000,
            [3] bool CS$4$0001
        )
 
        IL_0000: nop
        IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: ldc.i4.1
        IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
        IL_000e: nop
        IL_000f: ldloc.0
        IL_0010: ldc.i4.2
        IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
        IL_0016: nop
        IL_0017: nop
        IL_0018: ldloc.0
        IL_0019: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
        IL_001e: stloc.2
        .try
        {
            IL_001f: br.s IL_003c
            // loop start (head: IL_003c)
                IL_0021: ldloca.s CS$5$0000
                IL_0023: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
                IL_0028: stloc.1
                IL_0029: nop
                IL_002a: ldstr "value is {0}"
                IL_002f: ldloc.1
                IL_0030: box [mscorlib]System.Int32
                IL_0035: call void [mscorlib]System.Console::WriteLine(stringobject)
                IL_003a: nop
                IL_003b: nop
 
                IL_003c: ldloca.s CS$5$0000
                IL_003e: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
                IL_0043: stloc.3
                IL_0044: ldloc.3
                IL_0045: brtrue.s IL_0021
            // end loop
 
            IL_0047: leave.s IL_0058
        // end .try
        finally
        {
            IL_0049: ldloca.s CS$5$0000
            IL_004b: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
            IL_0051: callvirt instance void [mscorlib]System.IDisposable::Dispose()
            IL_0056: nop
            IL_0057: endfinally
        // end handler
 
        IL_0058: nop
        IL_0059: ret
    // end of method Program::LookatGenericList
 
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2190
        // Code size 7 (0x7)
        .maxstack 8
 
        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        IL_0006: ret
    // end of method Program::.ctor
 
// end of class boxOrUnbox.Program

C#装箱和拆箱(Boxing 和 UnBoxing)

1、什么是装箱和拆箱? 简单来说:       装箱是将值类型转换为引用类型 ;拆箱是将引用类型转换为值类型。(网上广为流传)  C#中值类型和引用类型的最终基类都是Object类型(它本身是一个引用...
  • qiaoquan3
  • qiaoquan3
  • 2016年05月17日 21:44
  • 2673

C#装箱和拆箱(Boxing 和 UnBoxing)

1、什么是装箱和拆箱? 简单来说:       装箱是将值类型转换为引用类型 ;拆箱是将引用类型转换为值类型。(网上广为流传)  C#中值类型和引用类型的最终基类都是Object类型(它本身是一个引用...
  • qiaoquan3
  • qiaoquan3
  • 2016年05月17日 21:44
  • 2673

关于装箱拆箱为什么会影响效率

概念 装箱在值类型向引用类型转换时发生,在堆中分配。 拆箱在引用类型向值类型转换时发生。 示例装箱 public void BoxIn() { ...
  • Admin_Jhon
  • Admin_Jhon
  • 2016年10月20日 15:55
  • 763

装箱和拆箱的基本原理

3.1.4 简述装箱和拆箱原理 40 · 装箱和拆箱的基本概念 · 装箱拆箱对性能的影响 · 如何有效避免装箱拆箱   分析问题 1.装箱和拆箱的基本概念 在第3.1.3节中,笔者已经介...
  • szyzxcv5689
  • szyzxcv5689
  • 2013年04月21日 16:31
  • 2669

浅C#中的装箱和拆箱

1、什么是装箱和拆箱? 简单的来说: 装箱就是值类型转换为引用类型;拆箱就是引用类型转换为值类型 值类型,包括原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、...
  • gengyudan
  • gengyudan
  • 2013年06月21日 09:53
  • 6611

.NET的装箱与拆箱内幕

装箱与拆箱是.NET中非常重要的概念。 装箱是将值类型转换成引用类型,或者是实现了接口的值类型。装箱将数据存储的空间由Thread stack转存到了Managed Heap中。凡是在Managed ...
  • xxdddail
  • xxdddail
  • 2014年07月04日 16:49
  • 1572

C#--三行代码带你理解神秘的拆箱和装箱

一、在说拆箱和装箱之前的准备知识首先,我们需要知道c#中有两种类型:值类型和引用类型 名称 值类型 引用类型 表示类型 基本类型 类,数组,接口 ,C#特有的委托. 存储内容...
  • qq_32452623
  • qq_32452623
  • 2016年12月29日 17:22
  • 2456

详解Java的自动装箱与拆箱(Autoboxing and unboxing)

一、什么是自动装箱拆箱 很简单,下面两句代码就可以看到装箱和拆箱过程//自动装箱 Integer total = 99;//自定拆箱 int totalprim = total;简单一点说,装箱就是...
  • hp910315
  • hp910315
  • 2015年09月22日 15:27
  • 4760

Java学习笔记之自动装箱与拆箱

更多博文可参考我的个人独立博客:贱贱的梦想 什么是自动装箱与拆箱自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Intege...
  • GongchuangSu
  • GongchuangSu
  • 2016年05月28日 22:28
  • 1100

深入剖析Java中的装箱和拆箱

原文出处: 海子 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱、拆箱相...
  • zf0512305
  • zf0512305
  • 2016年03月19日 14:39
  • 4128
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C# - 再谈C#的装箱和拆箱
举报原因:
原因补充:

(最多只允许输入30个字)