Discuss again the problem of packing and unpacking in C

  • 2020-05-10 18:45:11
  • OfStack

In the last article, we wrote 1 definition of boxing and unboxing and IL analysis. In this article, we looked at the situation of using generics and not using generics to cause boxing and unboxing
1. Boxing and unboxing operations when using non-generic collections
Take a look at the following code:

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 and adds two Numbers, 1, 2, to ArrayList. Then use foreach to print the elements from ArrayList to the console.
In this process, two boxing operations and two unboxing operations will occur. Boxing will occur when adding int type elements to ArrayList, unboxing will occur when enum int type elements in ArrayList with foreach, converting object type to int type, and twice more boxing operations will occur when Console.WriteLine is executed. This 1 section of code performs 6 boxed and unboxed operations; If the number of ArrayList elements is large, there will be more boxing and unboxing.
You can view the packing and unpacking process by using tools such as ILSpy to view the box and unbox instructions for the IL code
2. Use of generic collections
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 collection type USES the generic List instead of ArrayList. We can also view the case of boxing and unboxing by looking at the IL code, which only performs two boxing operations in the Console.WriteLine () method, no unboxing is required.
It can be seen that generics can avoid unnecessary performance consumption caused by packing and unpacking. Of course, the benefits of generics do not stop there. Generics can also increase the readability of programs, make them easier to reuse, and so on.
The C# code used in this article is as follows:

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);
            }
        }
    }
}

The IL code for C# is as follows:

.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(string, object)
                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(string, object)
                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

Related articles: