• 装箱与拆箱


    装箱(boxing):将值类型转换为引用类型。

    拆箱(unboxing):将引用类型转换为值类型。

    c#数据类型分:值类型【简单类型(布尔类型 字符类型 实数类型) 结构类型struct  枚举类型enum】

            引用类型【接口类型interface  所有的数组 类类型class 委托delegate】  指针类型

    为何需要装箱? 
    一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需

    要将一个值类型(如Int32)传入时,需要装箱。 
    另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据

    加入容器时,需要装箱。

    装箱的内部操作。 
    装箱: 对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。 
      第一步:新分配托管堆内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。 
      第二步:将值类型的实例字段拷贝到新分配的内存中。 
      第三步:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。 
    拆箱:检查对象实例,确保它是给定值类型的一个装箱值。将该值从实例复制到值类型变量

    中。

    在拆箱的过程中要注意以下两点:

    1. 如果对已装箱的值类型的引用的变量为null,会引发NullRefreenceException异常

    2. 如果一个引用指向的对象在拆箱时不是用的装箱时所使用的类型,将会引发InvalidCastException异常。代码如下:

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 Object o = x; 
    5 Int16 y = (Int16)o; //引发InvalidCastException异常
    6 }
    7


    正确的做法是,现将其用Int32类型来拆箱,然后再强制转换为Int16

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 Object o = x; 
    5 Int16 y = (Int16)(Int32)o; 
    6 }
     

    下面来看两段程序来深入理解下装箱和拆箱

    代码一:

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 Object o = x; 
    5 x = 123; 

    7 Console.WriteLine(x + ", " + (Int32)o); 
    8 }
    9


    上面的代码中有多少次装箱呢?乍一看好像就一次(Object o=x;),其实一共有三次装箱,看看IL代码就一目了然了。

    程序的执行步骤:

    1 创建一个Int32的未装箱的值类型实例x,并初始化为5.

    2 创建Object类型的变量o,并指向x。由于引用类型的变量必须要执行堆中的对象,所以要对x进行装箱(第一次装箱),并将x在堆中的引用地址存储在o中。

    3 将值123赋给未装箱的值类型实例x中。

    4 调用WriteLine方法,WriteLine方法的参数值类型为String,现在WriteLine方法存在三个数据项,值类型x、string类型“,”和一个已装箱的Int32类型实例的引用o,这三个数据项必须要合并成一个string对象才能被调用。

    5 调用String对象的静态方法Concat,Concat方法有9个重载,根据那三个数据项会选择下面方法执行。

    6 第一个参数arg0传入的是x ,参数类型为object,所以要对x进行装箱(第二次装箱),将引用地址传给arg0,arg1传入的是字符串“,”,字符串就是引用类型,直接传引用地址,arg2传入的是将o拆箱然后再装箱(第三次装箱)的引用地址传入。

    上面代码中的WriteLine方法如果直接写成Console.WriteLine(x + ", " + o); 将会有跟高的相率,因为o本身就是Object类型,在Concat的时候不用进行装箱拆箱。

    代码二:看看这段程序发生了几次装箱

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 Object o=x; 
    5 x=123; 
    6 Console.WriteLine(x); 
    7 x = (Int32)o; 
    8 Console.WriteLine(x); 
    9 Console.WriteLine(o); 
    10 }
    11


    上面的代码只发生了一次装箱,因为WriteLine方法的重载版本中参数类型可以为Objet或是Int32,在调用WriteLine方法是并没有装箱,唯一的一次装箱是Object o=x; 。

    代码三:

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 CheckRef(x, x); //输出不同引用
    5 }

    7 static void CheckRef(object obj1, object obj2)
    8 {
    9 if (obj1 == obj2)
    10 Console.WriteLine("相同引用"); 
    11 else
    12 Console.WriteLine("不同引用"); 
    13 }
    14

    1 static void Main(string[] args)
    2 {
    3 Int32 x = 5; 
    4 Object o = x; 
    5 CheckRef(o,o); //输出相同引用
    6 }

    8 static void CheckRef(object obj1, object obj2)
    9 {
    10 if (obj1 == obj2)
    11 Console.WriteLine("相同引用"); 
    12 else
    13 Console.WriteLine("不同引用"); 
    14 }
    15

    执行上面代码将发生两次装箱,因为CheckRef方法的两个参数都是Object类型,传入的都是值类型的实例,可以讲代码改进下,先将x转换成Object类型再传入方法,如下:

    改进后只进行一次装箱操作了,效率提高了,但是会发现运行的结果页发生了变化,所以这种做法在有些时候是很危险的。

    装箱拆箱操作极大的破环程序的性能,不过在Net2.0中提供了泛型集合类,所以完全可以用List 和Dictionary 来代替 原来1.0中的ArrayList和HashTable,即使是List也会比ArrayList的性能要好。

  • 相关阅读:
    mysql字符生命周期
    mysql5.6特殊字符问题
    电信网关-天翼网关-GPON-HS8145C设置桥接路由拨号认证
    linux-shell脚本高并发对文本url批量下载
    Kettle7.1在window启动报错
    微软的在线文档存储OneDrive使用帮助
    centos6.5搭建redmine3.4
    mysql基础拓扑图
    线上应用故障排查之一:高CPU占用
    线上服务CPU100%问题快速定位实战
  • 原文地址:https://www.cnblogs.com/eric-qin/p/4065395.html
Copyright © 2020-2023  润新知