【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用

news2025/4/17 19:19:53

文章目录

  • 一、值类型和引用类型汇总补充
    • 1、值类型和引用类型汇总
    • 2、值类型和引用类型的区别
    • 3、简单的判断值类型和引用类型
  • 二、变量的生命周期与性能优化
    • 1、**栈和堆的区别**
    • 2、**变量生命周期**
    • 3、**垃圾回收(GC)机制**
    • 4、**代码示例与优化**
      • 4.1. 临时变量的生命周期与回收
      • 4.2. 临时变量的性能问题:每次创建新变量
      • 4.3. 性能优化:减少不必要的变量创建
      • 4.4. 使用成员变量或者静态变量
    • 5、其他注意事项
    • 6、总结
  • 三、值类型和引用类型组合使用
    • 1、结构体中的值类型和引用类型
    • 2、类中的值类型和引用类型
    • 3、数组中的值类型和引用类型
    • 4、结构体继承接口
      • 4.1 示例
      • 4.2装箱拆箱
      • 4.3 性能考虑
    • 5、简单记忆口诀
  • 专栏推荐
  • 完结

一、值类型和引用类型汇总补充

1、值类型和引用类型汇总

我们学了很多新的值类型和引用类型,这里列个表全部分类一下,方便大家查看对比

类型类别类型描述
值类型byte无符号 8 位整数 (范围:0 到 255)
ushort无符号 16 位整数 (范围:0 到 65,535)
uint无符号 32 位整数 (范围:0 到 4,294,967,295)
ulong无符号 64 位整数 (范围:0 到 18,446,744,073,709,551,615)
sbyte有符号 8 位整数 (范围:-128 到 127)
short有符号 16 位整数 (范围:-32,768 到 32,767)
int有符号 32 位整数 (范围:-2,147,483,648 到 2,147,483,647)
long有符号 64 位整数 (范围:-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807)
float单精度浮点数 (32 位,范围大约为 ±1.5 x 10^−45 到 ±3.4 x 10^38)
double双精度浮点数 (64 位,范围大约为 ±5.0 × 10^−324 到 ±1.7 × 10^308)
decimal高精度十进制数(128 位,用于财务和其他需要高精度的应用)
char单一字符 (16 位 Unicode 字符)
bool布尔值 (truefalse)
enum枚举类型,定义一组命名常数值
struct结构体类型,可以包含字段、方法、属性等,常用于表示值对象
引用类型string字符串类型,表示文本,实际上是 char[] 的封装
array数组类型,可以是任何数据类型的集合
class类类型,用于定义对象的蓝图
interface接口类型,用于定义类和结构体的契约
delegate委托类型,用于定义引用方法的类型

2、值类型和引用类型的区别

  • 值类型

    • 存储:直接存储数据值。
    • 分配方式:在栈(stack)上分配内存。
    • 赋值行为:赋值时会复制数据,两个变量的修改不会相互影响。
  • 引用类型

    • 存储:存储的是数据的引用(即指向内存中数据的地址)。
    • 分配方式:在堆(heap)上分配内存。
    • 赋值行为:赋值时会复制引用,两个变量指向同一内存位置,修改其中一个会影响另一个。

3、简单的判断值类型和引用类型

可以看到前面有这么多的数据类型,记不住怎么办?

我们可以在编辑器按F12或者ctrl+鼠标左键点击进去类型的内部查看信息

  • 如果是class(类)就是引用类型
  • 如果是struct(结构体)就是值类型

比如int就是值类型
在这里插入图片描述
string就是引用类型
在这里插入图片描述


二、变量的生命周期与性能优化

1、栈和堆的区别

C# 中的变量存储在栈(Stack)和堆(Heap)上,这取决于变量的类型:

  • 值类型(Value Types):包括 intdoublestruct 等。值类型的变量直接存储数据本身,它们通常被分配在栈上。当超出作用域时,栈上的值类型会自动被回收。
  • 引用类型(Reference Types):包括 classstringarraydelegate 等。引用类型的变量存储的是指向实际数据(在堆上的对象)的引用(地址)。当引用类型变量超出作用域时,栈上的引用会被回收,但堆上的对象不会立即被销毁,而是会等待垃圾回收(GC)机制回收。

2、变量生命周期

  • 栈上值类型变量:当栈上的值类型变量超出作用域时,它会被立即销毁。
  • 堆上引用类型变量:引用类型的变量在栈上存储的是对象的地址,当栈上的引用类型变量超出作用域时,指向堆上对象的引用会被清除,但堆上的对象不会立刻被销毁,只有在垃圾回收(GC)时,它们才会被回收。

3、垃圾回收(GC)机制

  • 垃圾回收:C# 使用垃圾回收机制来自动管理内存,特别是对堆上的对象进行内存管理。当对象没有任何引用时,它会被垃圾回收器标记为垃圾并释放其占用的内存空间。
  • 值类型的回收:栈上存储的值类型变量会在超出作用域后自动销毁,不需要显式回收。
  • 引用类型的回收:栈上存储的引用会在超出作用域时被回收,但堆上的对象仍然需要垃圾回收器来管理。垃圾回收器会在堆上标记不再使用的对象并释放内存。

4、代码示例与优化

4.1. 临时变量的生命周期与回收

C# 中,当一个临时变量(如局部变量)超出其作用域时,它会被销毁,特别是在语句块(如函数、条件语句、循环语句等)执行结束时,栈上的局部变量会自动回收。

示例 1

void Example()
{
    int i = 5;  // 局部变量 i
}  // 变量 i 超出作用域后,会被销毁
  • Example 方法执行完后,i 超出了作用域,栈上分配给 i 的内存会被回收。

下面这里会报错的原因就是,栈是先进后出原则,{}包裹语句块,执行完成,i2就被回收了,所以外面打印不到i2内容
在这里插入图片描述

4.2. 临时变量的性能问题:每次创建新变量

在某些情况下,频繁创建临时变量可能会带来性能上的开销,尤其是在循环中。例如:

示例 2(性能问题):

while (true)
{
    int i = 1;  // 每次循环都会创建新的 i
}
  • 每次进入循环时,int i 都会在栈上分配内存,循环执行多次时,会频繁地分配和销毁 i,这会带来一定的性能开销。

4.3. 性能优化:减少不必要的变量创建

为了避免每次循环都重新创建新的变量,可以将变量声明移到循环外部。这样,变量只会被创建一次,循环内部只修改变量的值,而不需要反复创建。

优化方法 1

int i = 1;  // 移动到循环外部,减少变量创建次数
while (true)
{
    i = 1;  // 只是赋值,避免每次循环都重新创建变量
}
  • 这样,i 只会在循环外部创建一次,而每次循环只需要修改它的值,不会进行重复的内存分配和回收。

4.4. 使用成员变量或者静态变量

如果变量在多个方法或类实例之间共享,你可以考虑将变量声明为 成员变量静态变量。这可以避免频繁创建临时变量,提高性能。

优化方法 2(成员变量):

class Test
{
    int i;  // 成员变量

    public void TestMethod()
    {
        while (true)
        {
            i = 1;  // 只修改成员变量的值
        }
    }
}

优化方法 3(静态成员变量):

class Test
{
    static int i;  // 静态成员变量

    public void TestMethod()
    {
        while (true)
        {
            i = 1;  // 只修改静态变量的值
        }
    }
}
  • 成员变量:如果 i 只是与某个对象的状态相关,可以将 i 声明为成员变量。
  • 静态变量:如果 i 在所有对象之间共享,可以将其声明为静态变量。注意,静态变量是类级别的,而非实例级别的。

5、其他注意事项

  • 静态变量与垃圾回收:静态变量的生命周期与应用程序的生命周期相同,在整个程序运行期间,它们会一直存在,直到程序结束时才会被垃圾回收。
  • 避免不必要的内存分配:在高频率执行的循环中,尽量避免在每次循环中创建新变量,尤其是值类型。可以考虑将变量移到循环外部,或者使用静态变量和成员变量。

6、总结

  • 值类型:存储在栈上,超出作用域后会自动回收。
  • 引用类型:存储在堆上,栈上的引用超出作用域时会被回收,但堆上的对象直到垃圾回收器执行时才会被回收。
  • 性能优化:减少不必要的内存分配和销毁,避免在循环中频繁创建局部变量。可以通过将变量移到循环外部或使用成员变量、静态变量来优化性能。

三、值类型和引用类型组合使用

1、结构体中的值类型和引用类型

我们知道,在C#中,结构体 (struct) 是值类型存储在栈上,而引用类型(如 string)存储在堆上。那么结构体中的字段算是值类型还是引用类型呢?尤其是如何区分这两种类型的内存分配方式。

  • 结构体 (struct) 本身是值类型,因此当你复制结构体时,结构体内的所有字段(无论是值类型还是引用类型)都会被复制,而不是对象的实际内容。

  • 结构体中的值类型字段:存储的是实际的值,并且这个值直接存储在结构体的实例内存区域中。如果结构体作为方法参数传递,它会被 复制,因此对结构体字段的修改不会影响原始结构体。

  • 结构体中的引用类型字段:这些字段存储的是对堆中对象的引用(即地址)。即使结构体是值类型,结构体内部的引用类型字段仍然会引用堆中的对象。当结构体复制时,引用类型字段的引用会被复制,因此多个结构体实例可以引用同一个堆对象。

2、类中的值类型和引用类型

  • 类(class)是引用类型,意味着它的实例会在堆上分配内存,变量存储的是对对象的引用(地址),而不是对象的实际内容。

  • 类中的值类型字段:在类中的值类型字段(如 int、float 等)存储的是实际的值,这些值存储在对象的内存区域。因为类本身是引用类型,所以这些值会随着类的对象一起存储在堆上。

  • 类中的引用类型字段:这些字段存储的是对堆中对象的引用。当引用类型字段被赋值时,实际上是将引用的地址传递给另一个变量,因此修改一个引用类型字段会影响所有引用该对象的变量。

3、数组中的值类型和引用类型

  • 数组本身是引用类型:这意味着当你创建一个数组时,实际上是创建了一个指向堆(heap)中数据的引用。栈(stack)上只保存了这个引用,而实际的数据存储在堆中。
  • 类中的值类型字段:如果数组是值类型的数组(如 int[], double[]),那么数组中的每个元素都是独立的值类型实例,它们直接存储在数组所在的堆内存中。
  • 类中的引用类型字段:如果数组是引用类型的数组(如 string[], object[]),那么数组中的每个元素都是引用,指向堆中某个对象的实际位置。

4、结构体继承接口

在C#中,结构体(struct)可以实现接口,尽管结构体是值类型而接口是引用类型。

4.1 示例

比如我们新增一个结构体继承接口

interface ITest { 
    int Value {
        get;
        set;
    }
}

struct TestStruct : ITest
{
    private int value;
    public int Value { 
        get => value; 
        set => this.value = value; 
    }
}

根据里氏替换原则,父类可以装子类。所以我们可以用接口容器(父类)装载结构体(子类)。

TestStruct ts1 = new TestStruct();
ts1.Value = 1;
Console.WriteLine(ts1.Value);

TestStruct ts2 = ts1;
ts2.Value = 2;
Console.WriteLine(ts1.Value);
Console.WriteLine(ts2.Value);

ITest it1 = ts1;//装箱
ITest it2 = it1;
it2.Value = 100;
Console.WriteLine(it1.Value);
Console.WriteLine(it2.Value);

结果
在这里插入图片描述
根据结果我们可以发现,后面it1 it2的值打印都是100,相当于我们强行把值类型变成了引用类型。

4.2装箱拆箱

用接口容器装载结构体存在装箱拆箱。当你将一个实现了接口的结构体赋给接口类型的变量时,会发生装箱操作,即将值类型转换为引用类型。相反的过程称为拆箱。

TestStruct ts1 = new TestStruct();
ITest it1 = ts1;//装箱
TestStruct ts3 = (TestStruct)it1;//拆箱

4.3 性能考虑

频繁的装箱和拆箱会对性能产生负面影响,尤其是在循环或大量数据处理的情况下。为了避免这种性能问题,你可以考虑以下策略:

  • 使用类而不是结构体:如果需要频繁地将对象存储在接口容器中,考虑使用类而非结构体,以避免装箱开销。
  • 减少装箱次数:尽量减少不必要的装箱操作,例如通过缓存已经装箱的对象。
  • 泛型:使用泛型可以避免装箱。例如,List 可以持有值类型而不发生装箱。

5、简单记忆口诀

值类型跟大哥走,引用类型很自我


专栏推荐

地址
【从零开始入门unity游戏开发之——C#篇】
【从零开始入门unity游戏开发之——unity篇】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架开发】

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dockerfile运行指令

1.RUN 在build构建时执行命令。 举例:安装vim Shell命令格式 RUN yum install -y vim Exec命令格式 RUN ["yum","install","-y","vim"] 2.CMD 用于设置容器启动时默认执行的命令或参数。 如果Dockerfile中有多个CMD&a…

无穿戴动作捕捉系统技术解密及多元化运用

在当今科技飞速发展的时代,动作捕捉技术不断革新,无穿戴动作捕捉系统崭露头角。与传统粘贴标记点的动作捕捉技术相比,无标记点动作捕捉技术具有显著优势。它能够在确保高精度捕捉的前提下,以非接触的方式极大地提升被捕捉对象的表…

计算机网络 (10)网络层

前言 计算机网络中的网络层(Network Layer)是OSI(开放系统互连)模型中的第三层,也是TCP/IP模型中的第二层,它位于数据链路层和传输层之间。网络层的主要任务是负责数据包从源主机到目的主机的路径选择和数据…

基于Mosquito源码理解MQTT5.0的属性概念

MQTT 5.0协议相比之前的版本(如MQTT 3.1.1)增加了很多属性,这些属性分布于报文的可变头部(Variable Header)和有效载荷(Payload)中。这些属性大大增强了协议的可扩展性和灵活性,使其能够更好地适应现代物联网应用的复杂需求。 属性的定义在源码包mosquitto-2.0.18/inc…

rk3568之mpp开发笔记记录之摄像头实时码流获取的神秘面纱

前言: 大家好,在上一篇文章里面,我给大家解读了怎么获取imx415-sensor的实时码流的细节,今天开始会解析里面到底是怎么实现的? 提前透露一点,整个摄像头驱动框架和上层调用,都会涉及到v4l2的开发…

Mac电脑python多版本环境安装与切换

我当前是python3.9.6环境,需要使用3.9.8环境,通过brew安装3.9.8版本,然后通过pyenv切换环境 步骤 1: 安装 pyenv brew install pyenv brew install pyenv-virtualenv 步骤 2: 安装 Python 3.9.8(使用 pyenv 安装指定版本的 Pyth…

小程序配置文件 —— 14 全局配置 - tabbar配置

全局配置 - tabBar配置 tabBar 字段:定义小程序顶部、底部 tab 栏,用以实现页面之间的快速切换;可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面; 在上面图中,标注了一些 tabBar …

Jupyter在运行上出现错误:ModuleNotFoundError: No module named ‘wordcloud‘

问题分析:显示Jupyter未安装这个模板 解决办法:在单元格内输入:!pip install wordcloud

github提交不上去,网络超时问题解决

问题出现的原因: DNS服务器数据不同步,github的服务器发送迁移,在本地缓存的ip地址现在无效了。 解决方案: 1)点击这里,查询github.com最新的ip地址 2.0)编辑linux系统地址缓存文件&#x…

Windows Subsystem for Linux (WSL)

目录 定义与功能 版本与特点 应用场景 启用 WSL 功能 更新WSL及其内核 下载Linux发行版本 WSL(Windows Subsystem for Linux)是微软在Windows 10和Windows 11中引入的一项功能,使用户能够在Windows上原生运行Linux的命令行工具和应用程…

图像处理-Ch5-图像复原与重建

Ch5 图像复原 文章目录 Ch5 图像复原图像退化与复原(Image Degradation and Restoration)噪声模型(Noise Models)i.i.d.空间随机噪声(Generating Spatial Random Noise with a Specified Distribution)周期噪声(Periodic Noise)估计噪声参数(Estimating Noise Parameters) 在仅…

Mac 环境 VVenC 编译与编码命令行工具使用教程

VVenC VVenC 是一个开源的高效视频编码器,专门用于支持 H.266/VVC (Versatile Video Coding) 标准的编码。H.266/VVC 是继 HEVC (H.265) 之后的新一代视频编码标准,主要目的是提供比 HEVC 更高的压缩效率,同时保持或提高视频质量。H.266/VVC…

HTML——41有序列表

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>有序列表</title></head><body><!--有序列表&#xff1a;--><!--1.列表中各个元素在逻辑上有先后顺序&#xff0c;但不存在一定的级别关系-->…

详细教程:SQL2008数据库备份与还原全流程!

数据的安全性至关重要&#xff0c;无论是操作系统、重要文件、磁盘存储&#xff0c;还是企业数据库&#xff0c;备份都是保障其安全和完整性的关键手段。拥有备份意味着即使发生误删、系统崩溃或病毒攻击等问题&#xff0c;也能迅速通过恢复功能解决&#xff0c;避免数据丢失带…

IDEA工具使用介绍、IDEA常用设置以及如何集成Git版本控制工具

文章目录 一、IDEA二、建立第一个 Java 程序三、IDEA 常用设置四、IDEA 集成版本控制工具&#xff08;Git、GitHub&#xff09;4.1 IDEA 拉 GitHub/Git 项目4.2 IDEA 上传 项目到 Git4.3 更新提交命令 一、IDEA 1、什么是IDEA&#xff1f; IDEA&#xff0c;全称为 IntelliJ ID…

STM32 I2C通信协议

单片机学习&#xff01; 文章目录 目录 文章目录 前言 一、I2C通信 1.1 I2C总线 1.2 I2C通信线 1.3 同步半双工且数据应答 1.4 一主多从 二、硬件电路 2.1 I2C电路模型 2.2 I2C接线要求 2.3 I2C上拉电阻作用 三、I2C时序基本单元 3.1 起始终止条件 3.1.1 起始条件 3.1.2 终止条…

在线免费批量生成 Word 文档工具

为了方便的批量生成 Word 文档&#xff0c;写了个在线 Word 文档批量生成工具&#xff0c;可以根据 Excel 数据和 Word 模板批量生成大量个性化的 Word 文档。适用于需要批量生成格式统一但内容不同的文档场景。比如&#xff1a; 批量生成证书、奖状批量生成合同、协议批量生成…

再见了我的2024

目录 户外运动 阅读及影视剧欣赏 提升专业技能 外婆走了 消费分析 小妹家的二宝 2024 summary 2025年几个小目标 2024生活记录 2024年最后一天&#xff0c;就这样过去了。 总是来不及好好地告个别&#xff0c;就像我们的年老的亲人一样&#xff0c;见一面&#xff0c;少…

Java工程师实现视频文件上传minio文件系统存储及网页实现分批加载视频播放

Java工程师实现minio存储大型视频文件网页实现分批加载视频播放 一、需求说明 老板给我出个题目&#xff0c;让我把的电影文件上传到minio文件系统&#xff0c;再通过WEB端分配加载视频播放&#xff0c;类似于我们普通的电影网站。小编把Java代码共享出来。是真正的能拿过来直…

Three.js教程004:坐标辅助器与轨道控制器

文章目录 坐标辅助器与轨道控制器实现效果添加坐标辅助器添加轨道控制器完整代码完整代码下载坐标辅助器与轨道控制器 实现效果 添加坐标辅助器 创建坐标辅助器: const axesHelper = new Three.AxesHelper(5);添加到场景中: scene.