In-depth understanding of C# boxing and unboxing

I wrote it in the previous post Definition of boxing and unboxing and IL analysis , in this article, let's look at the case of using generics and not using generics to cause boxing and unboxing

1. Boxing and unboxing operations when using non-generic collections

Look at the following piece of code:

?
1 2 3 4 5 6 7 8 var array = new ArrayList(); array.Add(1); array.Add(2);   foreach (int value in array) { Console.WriteLine("value is {0}",value); }

The code declares an ArrayList object, adds two numbers 1, 2 to the ArrayList; then uses foreach to print the elements in the ArrayList to the console.

In this process, two boxing operations and two unboxing operations will occur. Boxing will occur when adding elements of type int to ArrayList, and unboxing will occur when using foreach to enumerate elements of type int in ArrayList. Convert the object type to int type, and perform two boxing operations when executing Console.WriteLine; this piece of code performs 6 boxing and unboxing operations; if the ArrayList has a large number of elements, execute There will be more operations for packing and unpacking.

You can view the boxing and unboxing process by using tools such as ILSpy to view the box and unbox instructions of the IL code

2. The case of using generic collections

Please see the following code:

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

The difference between the code and the code in 1 is that the type of the collection uses a generic List instead of an ArrayList; we can also check the boxing and unboxing by looking at the IL code. The above code will only be used in the Console.WriteLine() method. Perform 2 boxing operations, no unboxing operations are required.

It can be seen that generics can avoid unnecessary performance consumption caused by boxing and unboxing; of course, the benefits of generics are more than that, generics can also increase the readability of programs, make programs easier to reuse, and so on.

The C# code used in this article is as follows:

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4  
 5 namespace boxOrUnbox
 6 {
 7     class Program
 8     {
 9         static void Main(string[] args)
10         {
11             //do nothing
12         }
13  
14         static void Box()
15         {
16             object objValue = 9;
17         }
18  
19         static void Unbox()
20         {
21             object objValue = 4;
22             int value = (int)objValue;
23         }
24  
25         static void LookatArrayList()
26         {
27             var array = new ArrayList();
28             array.Add(1);
29             array.Add(2);
30  
31             foreach (int value in array)
32             {
33                 Console.WriteLine("value is {0}", value);
34             }
35         }
36  
37         static void LookatGenericList()
38         {
39             var list = new List<int>();
40             list.Add(1);
41             list.Add(2);
42  
43             foreach (int value in list)
44             {
45                 Console.WriteLine("value is {0}", value);
46             }
47         }
48     }
49 }

 

The IL code of C# is as follows:

  1 .class private auto ansi beforefieldinit boxOrUnbox.Program
  2     extends [mscorlib]System.Object
  3 {
  4     // Methods
  5     .method private hidebysig static
  6         void Main (
  7             string[] args
  8         ) cil managed
  9     {
 10         // Method begins at RVA 0x2050
 11         // Code size 2 (0x2)
 12         .maxstack 8
 13         .entrypoint
 14  
 15         IL_0000: nop
 16         IL_0001: ret
 17     } // end of method Program::Main
 18  
 19     .method private hidebysig static
 20         void Box () cil managed
 21     {
 22         // Method begins at RVA 0x2054
 23         // Code size 10 (0xa)
 24         .maxstack 1
 25         .locals init (
 26             [0] object objValue
 27         )
 28  
 29         IL_0000: nop
 30         IL_0001: ldc.i4.s 9
 31         IL_0003: box [mscorlib]System.Int32
 32         IL_0008: stloc.0
 33         IL_0009: ret
 34     } // end of method Program::Box
 35  
 36     .method private hidebysig static
 37         void Unbox () cil managed
 38     {
 39         // Method begins at RVA 0x206c
 40         // Code size 16 (0x10)
 41         .maxstack 1
 42         .locals init (
 43             [0] object objValue,
 44             [1] int32 'value'
 45         )
 46  
 47         IL_0000: nop
 48         IL_0001: ldc.i4.4
 49         IL_0002: box [mscorlib]System.Int32
 50         IL_0007: stloc.0
 51         IL_0008: ldloc.0
 52         IL_0009: unbox.any [mscorlib]System.Int32
 53         IL_000e: stloc.1
 54         IL_000f: ret
 55     } // end of method Program::Unbox
 56  
 57     .method private hidebysig static
 58         void LookatArrayList () cil managed
 59     {
 60         // Method begins at RVA 0x2088
 61         // Code size 114 (0x72)
 62         .maxstack 2
 63         .locals init (
 64             [0] class [mscorlib]System.Collections.ArrayList 'array',
 65             [1] int32 'value',
 66             [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
 67             [3] bool CS$4$0001,
 68             [4] class [mscorlib]System.IDisposable CS$0$0002
 69         )
 70  
 71         IL_0000: nop
 72         IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
 73         IL_0006: stloc.0
 74         IL_0007: ldloc.0
 75         IL_0008: ldc.i4.1
 76         IL_0009: box [mscorlib]System.Int32
 77         IL_000e: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
 78         IL_0013: pop
 79         IL_0014: ldloc.0
 80         IL_0015: ldc.i4.2
 81         IL_0016: box [mscorlib]System.Int32
 82         IL_001b: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
 83         IL_0020: pop
 84         IL_0021: nop
 85         IL_0022: ldloc.0
 86         IL_0023: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator()
 87         IL_0028: stloc.2
 88         .try
 89         {
 90             IL_0029: br.s IL_004a
 91             // loop start (head: IL_004a)
 92                 IL_002b: ldloc.2
 93                 IL_002c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
 94                 IL_0031: unbox.any [mscorlib]System.Int32
 95                 IL_0036: stloc.1
 96                 IL_0037: nop
 97                 IL_0038: ldstr "value is {0}"
 98                 IL_003d: ldloc.1
 99                 IL_003e: box [mscorlib]System.Int32
100                 IL_0043: call void [mscorlib]System.Console::WriteLine(string, object)
101                 IL_0048: nop
102                 IL_0049: nop
103  
104                 IL_004a: ldloc.2
105                 IL_004b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
106                 IL_0050: stloc.3
107                 IL_0051: ldloc.3
108                 IL_0052: brtrue.s IL_002b
109             // end loop
110  
111             IL_0054: leave.s IL_0070
112         } // end .try
113         finally
114         {
115             IL_0056: ldloc.2
116             IL_0057: isinst [mscorlib]System.IDisposable
117             IL_005c: stloc.s CS$0$0002
118             IL_005e: ldloc.s CS$0$0002
119             IL_0060: ldnull
120             IL_0061: ceq
121             IL_0063: stloc.3
122             IL_0064: ldloc.3
123             IL_0065: brtrue.s IL_006f
124  
125             IL_0067: ldloc.s CS$0$0002
126             IL_0069: callvirt instance void [mscorlib]System.IDisposable::Dispose()
127             IL_006e: nop
128  
129             IL_006f: endfinally
130         } // end handler
131  
132         IL_0070: nop
133         IL_0071: ret
134     } // end of method Program::LookatArrayList
135  
136     .method private hidebysig static
137         void LookatGenericList () cil managed
138     {
139         // Method begins at RVA 0x2118
140         // Code size 90 (0x5a)
141         .maxstack 2
142         .locals init (
143             [0] class [mscorlib]System.Collections.Generic.List`1<int32> list,
144             [1] int32 'value',
145             [2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> CS$5$0000,
146             [3] bool CS$4$0001
147         )
148  
149         IL_0000: nop
150         IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
151         IL_0006: stloc.0
152         IL_0007: ldloc.0
153         IL_0008: ldc.i4.1
154         IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
155         IL_000e: nop
156         IL_000f: ldloc.0
157         IL_0010: ldc.i4.2
158         IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
159         IL_0016: nop
160         IL_0017: nop
161         IL_0018: ldloc.0
162         IL_0019: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
163         IL_001e: stloc.2
164         .try
165         {
166             IL_001f: br.s IL_003c
167             // loop start (head: IL_003c)
168                 IL_0021: ldloca.s CS$5$0000
169                 IL_0023: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
170                 IL_0028: stloc.1
171                 IL_0029: nop
172                 IL_002a: ldstr "value is {0}"
173                 IL_002f: ldloc.1
174                 IL_0030: box [mscorlib]System.Int32
175                 IL_0035: call void [mscorlib]System.Console::WriteLine(string, object)
176                 IL_003a: nop
177                 IL_003b: nop
178  
179                 IL_003c: ldloca.s CS$5$0000
180                 IL_003e: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
181                 IL_0043: stloc.3
182                 IL_0044: ldloc.3
183                 IL_0045: brtrue.s IL_0021
184             // end loop
185  
186             IL_0047: leave.s IL_0058
187         } // end .try
188         finally
189         {
190             IL_0049: ldloca.s CS$5$0000
191             IL_004b: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
192             IL_0051: callvirt instance void [mscorlib]System.IDisposable::Dispose()
193             IL_0056: nop
194             IL_0057: endfinally
195         } // end handler
196  
197         IL_0058: nop
198         IL_0059: ret
199     } // end of method Program::LookatGenericList
200  
201     .method public hidebysig specialname rtspecialname
202         instance void .ctor () cil managed
203     {
204         // Method begins at RVA 0x2190
205         // Code size 7 (0x7)
206         .maxstack 8
207  
208         IL_0000: ldarg.0
209         IL_0001: call instance void [mscorlib]System.Object::.ctor()
210         IL_0006: ret
211     } // end of method Program::.ctor
212  
213 } // end of class boxOrUnbox.Program

 

************ Reprinted: https://www.cnblogs.com/yukaizhao/archive/2004/01/13/csharp_box_unbox_2.html

Tags: C#

Posted by matthewd on Tue, 17 May 2022 10:19:36 +0300