C# 装箱拆箱 以及性能方面的问题

C#中的拆箱与装箱_夜槿笙歌的博客-CSDN博客_c#拆装箱

1.什么是拆箱和装箱

在C#中,值类型是直接将数据存储在栈空间中;而引用类型是将数据存储在堆空间中,同时在栈空间中存储一个对该数据的引用。那么如果将一个值类型转换为一个它实现的某个接口或object会发生什么?结果必然是对一个存储位置的引用,且这个存储位置表面上存储的是这个引用类型的实例,实际上则是存储的值类型的值。这个转换称为装箱。相反的过程称为拆箱。

int number = 10;  
// 装箱  
object obj = number;  
// 拆箱  
number = (int) obj;

    1
    2
    3
    4
    5

装箱(从值类型转换到引用类型)需要经历如下几个步骤:

    首先在堆上分配内存。这些内存主要用于存储值类型的数据。
    接着发生一次内存拷贝动作,将当前存储位置的值类型数据拷贝到堆上分配好的位置。
    最后返回对堆上的新存储位置的引用。

拆箱(从引用类型转换为值类型)的步骤则相反:

    首先检查已装箱的值的类型兼容目标类型。
    接着发生一次内存拷贝动作,将堆中存储的值拷贝到栈上的值类型实例中。
    最后返回这个新的值。

2.频繁拆装箱导致性能问题

由于拆箱和装箱都会涉及到一次内存拷贝动作,因此频繁地进行拆装箱会大幅影响性能。不同于拆箱需要进行强制类型转换,装箱由于包含隐式的类型转换而更容易被忽视。
下面来看一个例子:

int count = 10;  
ArrayList list = new ArrayList();  
list.Add(count);  
list.Add(20);  
int sum = (int) list[0] + (int) list[1];  
Console.Write("结果为:{0}",sum);

    1
    2
    3
    4
    5
    6

上面这段代码我们通过查看CIL就能发现,其总共执行了3次装箱和2次拆箱操作。

    首先是ArrayList的Add方法,其参数为object类型,因而添加int类型的对象时会执行装箱操作,总共执行了2次。
    当计算集合中的元素之和时,由于集合中存储的是object类型的数据,因此需要进行强制类型转换,造成了拆箱操作,总共执行了2次。
    当调用Console.Write方法时,它的签名为 void Write(string format, object arg),包含从int类型到object类型的装箱操作,共1次。

因此要注意这种“不起眼”的隐式类型转换。
3.避免可变值类型

我们来看一段代码:

struct Vector3
{
    public float X { get; set; }
    public float Y { get; set; }
    public float Z { get; set; }

    public void MoveTo(int x, int y, int z)
    {
        X = x;
        Y = y;
        Z = z;
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

Vector3 vector3 = new Vector3() {X = 10, Y = 20, Z = 30};  
object objVector3 = vector3;  
((Vector3) objVector3).MoveTo(40,50,60);  
Console.WriteLine($"objVector3 is ({((Vector3) objVector3).X},{((Vector3) objVector3).Y},{((Vector3) objVector3).Z})");

    1
    2
    3
    4

按照惯性思维我们可能认为输出为objVector3 is (40,50,60),但实际的输出却是objVector3 is (10,20,30)。为什么呢?

首先,Vector3是值类型,我们创建了一个实例,并将其赋值为(10,20,30)。然后将其赋值给一个object类型的变量,此时编译器会进行一次装箱,会拷贝一份数据到堆空间中,并返回对该空间的引用。而为了调用MoveTo方法,编译器又对objVector3进行了拆箱,并创建了一份值的拷贝。调用MoveTo方法时,改变的是这份拷贝的值,而原引用指向的堆空间中的数据并没有被修改。

从这个例子可以看出,可变值类型容易让人产生迷惑,因为往往修改的是值的拷贝而非本体。因此要尽量避免设计这种可变值类型。

参考文献:
[1]马克·米凯利斯.C#8.0本质论[M].机械工业出版社.
————————————————
版权声明:本文为CSDN博主「夜槿笙歌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LWR_Shadow/article/details/125705438

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值