再议C#中的装箱与拆箱的问题详解

 更新时间:2020年6月25日 11:41  点击:2207
上一篇写了一下装箱拆箱的定义和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 value in list)
{
Console.WriteLine("value is {0}", value);
}

代码和1中的代码的差别在于集合的类型使用了泛型的List,而非ArrayList;我们同样可以通过查看IL代码查看装箱拆箱的情况,上述代码只会在Console.WriteLine()方法时执行2次装箱操作,不需要拆箱操作。
可以看出泛型可以避免装箱拆箱带来的不必要的性能消耗;当然泛型的好处不止于此,泛型还可以增加程序的可读性,使程序更容易被复用等等。
本文使用的C#代码如下:
复制代码 代码如下:

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代码如下:
复制代码 代码如下:

.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
[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25