c# 引用类型还是值类型代码解析

最近笔者面试一些人,发现很多工作10年的人一些基础问题都答不好

记得当年自己大学刚学编程时,侯捷老师的《深入浅出MFC》开篇第一句“勿在浮沙筑高台”。项目如同建筑,下一层必须比上一层坚固,否则会垮塌。

如果我问你class类型和struct类型是引用类型还是值类型,你会要思考一下么,当然如果你是大佬请绕道

我们来复习一下c#中引用类型和值类型的一些基础概念:

在 C# 中,值类型和引用类型是两种不同的类型。它们有以下几个主要区别:

  1. 存储方式:值类型的实例直接存储在栈上,而引用类型的实例存储在堆上,栈上只存储引用(指向堆中实例的指针)。

  1. 传递方式:值类型的实例在传递时是通过值拷贝传递的,而引用类型的实例在传递时是通过引用拷贝传递的。

  1. 生命周期:值类型的实例的生命周期与其所在的作用域相同,而引用类型的实例的生命周期可能会超过其所在的作用域,因为引用可能会被传递到其他作用域中。

  1. 默认值:值类型的默认值是其零值(例如,整数类型的默认值为0),而引用类型的默认值是 null

  1. 比较方式:值类型的比较是按值比较的,而引用类型的比较是按引用比较的(即比较两个引用是否指向同一个对象)。

需要注意的是,C# 中的一些类型(如字符串)虽然是引用类型,但是它们的行为更像值类型,因为它们的实例在不可变时可以被重用,而不需要在堆上创建新的实例。此外,结构体(struct)也是值类型,但是可以像类一样拥有方法和属性。

    Type[] types = new Type[] {
                typeof(int),
                typeof(string),
                typeof(object),
                typeof(decimal),
                typeof(double),
                typeof(float),
                typeof(structs),
                typeof(Program)
            };

    foreach (Type type in types)
    {
        Console.WriteLine($"{type.Name} is{(type.IsValueType ? " not" : "")} a reference type");
    }

好的,看完上面,你应该有所新的收获,现在在问你一个问题,一个类里面的一个int型数据是值类型还是引用类型呢?

我们根据这个在复习下装箱和拆箱的概念

在 Unity 中,装箱和拆箱与在标准的 C# 中是相同的。装箱是指将值类型(例如结构体)转换为引用类型(例如对象),而拆箱是指将引用类型转换回值类型。

下面是一些关于装箱和拆箱在 Unity 中的简单说明:

  • 装箱:装箱是将值类型转换为对象类型的过程。当你将一个值类型(例如 intfloat 或自定义结构体)赋值给一个对象类型(例如 objectSystem.Collections.ArrayList)时,就会发生装箱。装箱操作会在堆上创建一个新的对象,并将值类型的实例复制到新的对象中。这可能会导致性能问题,因为它需要进行额外的内存分配和复制操作。

  • 拆箱:拆箱是将对象类型转换回值类型的过程。当你从一个对象类型(例如 objectSystem.Collections.ArrayList)中提取一个值类型时,就会发生拆箱。拆箱操作将从对象中提取值类型实例,并将其复制到一个新的值类型变量中。这也可能会导致性能问题,因为它需要进行额外的内存分配和复制操作。

因此,在 Unity 中,尽量避免进行频繁的装箱和拆箱操作,特别是在性能敏感的代码路径中,因为它们可能会对游戏性能产生负面影响。如果可能的话,可以使用泛型类型(例如 List<T>Dictionary<TKey, TValue> 等)来避免装箱和拆箱操作,因为它们可以处理任意类型的值类型,而不需要进行装箱和拆箱操作。

拆箱为啥消耗性能:

拆箱(Unboxing)是将值类型从对象类型转换回原始值类型的过程。在 C# 中,值类型被封装为对象类型(装箱)后,如果需要使用原始值类型,就需要进行拆箱操作。拆箱是一种显式的类型转换,因为值类型和对象类型之间是不兼容的。

拆箱操作消耗性能的原因主要有以下几点:

  1. 类型转换:拆箱是一种从 object 类型到具体值类型的转换过程,需要进行类型检查和转换操作。这涉及到额外的运行时检查和计算,导致了性能损失。

  1. 内存复制:拆箱操作通常涉及将堆上的值类型复制到栈上或堆上的另一个位置,以便在使用时能够直接访问值类型的数据。这涉及到额外的内存复制,增加了处理时间和内存开销。

  1. 垃圾回收(GC)压力:拆箱可能导致频繁的装箱和拆箱操作,这意味着在堆上创建和销毁对象,增加了垃圾回收的负担。频繁的垃圾回收可能导致应用程序性能下降。

  1. 值类型不适合装箱:值类型本身设计为在栈上分配内存,以便快速访问和高性能。但装箱操作将其转换为引用类型,使得它们在堆上分配内存,而且无法直接访问,从而降低了性能。

为了避免拆箱操作,应该尽量避免在值类型和 object 类型之间频繁进行转换。如果确实需要在值类型和引用类型之间进行转换,可以使用泛型和装箱技术来优化性能。使用泛型可以避免装箱,从而提高性能。同时,了解在什么情况下会发生装箱和拆箱操作也是优化性能的重要一环。

问题来了,当你从一个struct类型去访问object父节点类型里面的一个方法Equals方法,是拆箱还是装箱呢?

答案我会在我的公众号公布,回复"拆箱"获得

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值