• 装箱和拆箱


    装箱和拆箱

    值类型变量在线程堆栈上分配存储空间,然而由于其派生自Object类,所以可以用一个Object类变量存放一个值类型数据。请看以下代码:

    int i = 123;

    object o = i;

    很明显,第2句代码将值类型的数据“123”放到了一个Object类型的变量o中,而o是一个引用类型变量,其引用的对象必须存活于托管堆中。为了解决这个问题,CLR将值类型的数据“包裹”到一个匿名的托管对象中,并将此托管对象的引用放在Object类型的变量o中,这个过程称为“装箱(Boxing)”,装箱后对象的内存布局如图4-4所示。

    事实上,CLR为所有的值类型变量都提供了一个对应的“装箱”数据类型,此数据类型其实是一个类,拥有与值类型相同的数据和行为。

    需要注意的是,装箱之后的变量i与变量o是两个完全独立的变量,只是初值相同罢了,对任何一个变量值的改变不会影响另一个。

    1   int i = 123;     

    2   object o = i;        // 装箱:boxing

    3   int j = (int) o;    // 拆箱:unboxing

    上述第3句代码将装箱后的数据再“拆箱(Unboxing)”,将其值赋给变量j,参见图4-5。

    “装箱”与“拆箱”使我们可以把值类型变量看成是引用类型变量,但这个操作是耗时的,会影响程序的运行性能,因此,应该尽量避免在程序中使用装箱与拆箱操作。

     深入内幕

    装箱与拆箱的技术内幕

    (1)装箱

    装箱转换允许将值类型(Value Type)变量隐式转换为引用类型(Reference Type)变量。

    将值类型变量的一个值装箱包括以下操作:分配一个对象实例,然后将值类型变量的值复制到该实例中。

    可以用以下方法理解实际装箱过程:设想有一个特殊的装箱类(Boxing Class)。对任何值类型的类型T而言,装箱类的行为可描述如下:

    sealed class T_Box: System.ValueType

    {

        T value;

        public T_Box(T t)

        {

            value = t;

        }

    }

    下面的装箱语句:

    int i = 123;

    object box = i;

    在概念上相当于

    int i = 123;

    object box = new int_Box(i);

    实际上,像上面这样的T_Box和int_Box并不存在,并且装了箱的值的动态类型也不会真的属于一个类类型。相反,类型T的装了箱的值属于值类型T。例如:

    int i = 123;

    object box = i;

    if (box is int) //装箱后的变量仍是int类型

    {

        Console.Write("Box对象包含一个int型数据");

    }

    上述代码在运行时将在控制台上输出字符串“Box对象包含一个int型数据”。

    装箱转换隐含着复制一份待装箱的值。这不同于从引用类型到Object类型的转换,在后一种转换中,转换后的值继续引用同一实例,只是将它当作派生程度较小的Object类型而已。例如,以下代码声明了一个值类型Point。

    struct Point

    {

        public int x, y;

        public Point(int x, int y)

        {

            this.x = x;

            this.y = y;

        }

    }

    则下面的语句

    Point p = new Point(10, 10);

    object box = p;

    p.x = 20;

    Console.Write(((Point)box).x);

    将在控制台上输出值10,因为将p赋值给box是一个隐式装箱操作,它将复制p的值。但如果将Point声明为class,由于p和box将引用同一个实例,因此输出值为20。

    (2)拆箱

    拆箱是装箱的逆过程。

    一个拆箱操作包括以下两个步骤:

    * 检查对象实例是否为给定值类型的一个装了箱的值。

    * 将该值从实例中复制出来。

    参照前面的例子,下面的语句

    object box = 123;

    int i = (int)box; //拆箱

    在概念上相当于

    object box = new int_Box(123);

    int i = ((int_Box)box).value;

    为了使给定值类型拆箱转换在运行时取得成功,源操作数的值必须是对某个对象的引用,而该对象先前是通过将该值类型的某个值装箱而创建的。

    q 如果源操作数为null,则将引发System.NullReferenceException异常。

    q 如果源操作数是对不兼容对象的引用,则将引发System.InvalidCastException异常。

    IL为装箱与拆箱分别准备了两条指令:Box和Unbox,读者可以查询Visual Studio 2005文档了解详情。



  • 相关阅读:
    tomcat9.x 集群升级至 tomcat 10.x 发现的问题....
    java8 快速实现List转map 、分组、过滤等操作
    java高亮显示关键字不区分大小写(但不改变原文字母的大小写)---关键字分词功能(自写算法:关键字之间有子集的情况和关键字首尾拼接)
    Java Array、List、Set互相转化
    java 查找list中重复数据
    Java Set对象去重
    Java--如何高效向List中存放不重复的数据(附带时间测试)
    java list的交集,差集,并集
    Java中枚举实现单例模式
    使用jsoup选择器来查找元素
  • 原文地址:https://www.cnblogs.com/itgmhujia/p/1145287.html
Copyright © 2020-2023  润新知