当前位置: 首页 > news >正文

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

文章目录

  • 一、值类型和引用类型汇总补充
    • 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

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

相关文章:

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

文章目录 一、值类型和引用类型汇总补充1、值类型和引用类型汇总2、值类型和引用类型的区别3、简单的判断值类型和引用类型 二、变量的生命周期与性能优化1、**栈和堆的区别**2、**变量生命周期**3、**垃圾回收(GC)机制**4、**代码示例与优化**4.1. 临时…...

从论文到实践:Stable Diffusion模型一键生成高质量AI绘画

🏡作者主页:点击! 🤖编程探索专栏:点击! ⏰️创作时间:2024年12月24日10点02分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 论文源地址有视频: 链接h…...

项目管理:用甘特图 “导航” 项目全程

项目全程管理是一个复杂而又系统的过程,它涵盖了从项目启动到结束的各个阶段,包括规划、执行、监控和收尾等一系列活动。 项目全程管理能够确保项目按时交付、控制成本、提高质量以及满足客户需求。通过有效的管理,项目团队可以避免资源浪费…...

v3.0.8- 「S+会员」新增专属运动秀,试试新穿搭吧- 与「好友」

v3.0.8 - 「S会员」新增专属运动秀,试试新穿搭吧 - 与「好友」互动支持前往对方所在的「在线运动房」 - 「运动秀工坊」新增智能背景抠图 - 「体育竞技」匹配中可以看到我和对手的装备 - 多项界面体验和性能优化 v2.0.17 - 班级运动场新增运动秀展示 - 班级玩法&…...

9-Gin 中自定义 Model --[Gin 框架入门精讲与实战案例]

在 Gin 框架中自定义 Model 通常指的是定义你自己的数据结构,这些结构体(Structs)将用来表示数据库中的表、API 请求的参数或响应的数据格式。下面是如何在 Gin 中创建和使用自定义 Model 的基本步骤。 自定义 Model 定义结构体 首先&…...

【VBA】EXCEL - VBA 创建 Sheet 表的 6 种方法,以及注意事项

目录 1. 创建一个新工作表,并将其添加到工作簿的末尾 2. 创建一个新工作表,并命名它 3. 创建一个新工作表,并将其插入到指定位置 4. 检查是否已有同名工作表,避免重复创建 5. 创建多个工作表 6. 基于现有模板创建新工作表 …...

数据库中,group by 和partition by:数据分组和数据分区的区别

数据库中,group by 和partition by:数据分组和数据分区的区别 在大规模数据处理和分析的场景中,对数据进行分区和分组处理是非常常见的场景。 为了实现这一操作,在一些主流的关系型数据库管理系统中,提供了group by 和…...

【linux学习指南】Ext系列文件系统(四)路径分区链接

文章目录 🌠⽬录与⽂件名🌠路径解析🌠路径缓存🌠挂载分区🌉 ⽂件系统总结 🌠软硬连接🌉 硬链接🌉 软链接🌉 软硬连接对⽐🌉软硬连接的⽤途: &…...

深度学习中的参数初始化

深度学习中的参数初始化主要是指初始化神经网络中的权重和偏置。权重和偏置通常分开初始化,偏置通常初始化为零或较小的常数值。 没有一种万能的初始化技术,因为最佳初始化可能因具体架构和要解决的问题而异。因此,尝试不同的初始化技术以了解…...

wpf 基于Behavior库 的行为模块

Microsoft.Xaml.Behaviors 是一个用于WPF(Windows Presentation Foundation)的行为库,它的主要作用是允许开发者在不修改控件源代码的情况下,为控件添加自定义的行为和交互逻辑。行为库的核心思想是通过定义可重用的行为组件&…...

【每日学点鸿蒙知识】导入cardEmulation、自定义装饰器、CallState状态码顺序、kv配置、签名文件配置

1、HarmonyOS 无法导入cardEmulation? 在工程entry mudule里的index.ets文件里导入cardEmulation失败 可以按照下面方式添加SystemCapability;在src/main/syscap.json(此文件需要手动创建)中添加如下内容 {"devices": {"gen…...

【SpringMVC】REST 风格

REST(Representational State Transfer,表现形式状态转换)是一种访问网络资源的格式。传统的资源描述方式通常如下: http://localhost/user/getById?id1http://localhost/user/saveUser 而 REST 风格的描述则更简洁&#xff1a…...

IDEA修改编译版本

目录 一、序言 二、修改maven配置 1.修改 2.代码 三、pom文件配置 1.修改 2.代码 3.问题 一、序言 有两种方法可以帮助大家解决IDEA每次刷新maven的pom配置时,会发生发行源版本不正常的报错。个人推荐第二种,原因:第二种你刷新maven后…...

SkyWalking Agent 配置 Spring Cloud Gateway 插件解决日志错误

SkyWalking Agent 配置 Spring Cloud Gateway 插件解决日志错误 IDEA中启动网管时,需要配置VM启动参数,格式如下: # 配置 SkyWalking Agent 启动参数,以便将网关服务的性能数据上报到 SkyWalking 服务器。 -javaagent:/path/to/sk…...

canvas+fabric实现时间刻度尺(一)

前言 需求:显示一个时间刻度尺,鼠标移动会显示当前时间 技术:我们采用canvasfabric进行实现 效果 实现 1.创建canvas(设置宽高)设为全局变量 2.引入fabric包 3.画时间刻度尺(长方形横线) …...

傲雷亮相2024中国时尚体育季(珠海站),展现户外移动照明风采

2024年12月28-29日,2024中国时尚体育季(珠海站)国家级轮滑比赛在珠海金山体育公园成功举办。作为户外创新型移动照明领域的领导品牌,傲雷受邀参加了本次珠海金湾运动生活嘉年华的展览单元,与众多户外运动品牌同台展示。…...

YOLOv10-1.1部分代码阅读笔记-block.py

block.py ultralytics\nn\modules\block.py 目录 block.py 1.所需的库和模块 2.class DFL(nn.Module): 3.class Proto(nn.Module): 4.class HGStem(nn.Module): 5.class HGBlock(nn.Module): 6.class SPP(nn.Module): 7.class SPPF(nn.Module): 8.class C1(nn…...

@RestControllerAdvice注解

RestControllerAdvice 是 Spring 4 引入的一个组合注解,它结合了 ControllerAdvice 和 ResponseBody,专门用于处理 RestController 类型的控制器中的全局异常、全局数据绑定和全局模型属性等问题。在 Spring Boot 中,RestControllerAdvice 通…...

Enum枚举类与静态变量和静态数组的区别

Enum枚举类与静态变量和静态数组的区别 组成结构Enum枚举类静态变量静态数组 组成结构的区别相同之处不同之处 用法使用相同之处不同之处 组成结构 先来看下Enum枚举类,静态变量,静态数组的初始化过程,以下面为例子: public enu…...

uniapp——微信小程序读取bin文件,解析文件的数据内容(三)

微信小程序读取bin文件内容 读取用户选择bin文件,并解析数据内容,分包发送给蓝牙设备; 文章目录 微信小程序读取bin文件内容读取文件读取内容返回格式 API文档: getFileSystemManager 关于App端读取bin文件,请查看&…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...

管理学院权限管理系统开发总结

文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

Selenium常用函数介绍

目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件,这次有个需求是要兼容xp环境,xp上chrome最高支持到49,虽然说iframe或者embed都可以实现预览pdf,但为了后续的定制化需求,还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...