【从零开始入门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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

65.基于SpringBoot + Vue实现的前后端分离-阿博图书馆管理系统(项目 + 论文PPT)

项目介绍 随着社会的发展,计算机的优势和普及使得阿博图书馆管理系统的开发成为必需。阿博图书馆管理系统主要是借助计算机,通过对图书借阅等信息进行管理。减少管理员的工作,同时也方便广大用户对所需图书借阅信息的及时查询以及管理。 本站…

系统报错:由于找不到msvcp140.dll msvcp140_1.dll无法继续执行问题解决

Java资深小白,不足之处,或者有任何错误欢迎指出。 --蓝紫电脑重装后安装mysql,在执行时mysqld -install时出现系统报错:由于找不到msvcp140.dll无法继续执行、由于找不到msvcp140_1.dll无法继续执行。 尝试了其他博主提出的解决方案要么无效…

【再谈设计模式】策略模式 ~ 算法与行为的灵活调度员

本章内容思维导图: ​ 一、引言 在软件工程,软件开发过程中,我们常常会遇到这样的情况:需要根据不同的条件或者用户输入来执行不同的算法或者操作流程。例如,在一个电商系统中,根据用户的会员等级&#xff…

019-spring-基于aop的事务控制原理

1、事务配置&#xff1a; <tx:annotation-driven transaction-manager"transactionManager"/> transaction-manager 默认是找这个bean&#xff1a;transactionManager 2、从命名空间开始找到对应的解析配置如下&#xff1a; 对应的是这个 后续跟源码没有搞明…

Cursor登录按钮点击没反应

问题 系统&#xff1a;Windows11 Cursor&#xff1a;Cursor 0.44.9 当安装Cursor打开进行登录时&#xff0c;点击Sign in没反应 解决方案 1.打开window11的设置 2.点击应用中的默认应用 3.在设置应用程序的默认值中搜索Google&#xff08;没有Google浏览器的尝试下载一个&a…

30分钟搭建 Typecho 个人博客教程

Typecho是一款PHP博客程序&#xff0c;相比于WordPress&#xff0c;Typecho显得更加的轻量级和简洁。现在越来越多的人倾向于用Typecho来搭建个人博客——众所周知&#xff0c;能跑WordPress的机器都不便宜。 Typecho是一款国人团结打造的开源博客系统&#xff0c;和WordPress…

机器学习详解(11):分类任务的模型评估标准

模型评估是利用不同的评估指标来了解机器学习模型的性能&#xff0c;以及其优势和劣势的过程。评估对于确保机器学习模型的可靠性、泛化能力以及在新数据上的准确预测能力至关重要。 文章目录 1 介绍2 评估准则3 分类指标3.1 准确率 (Accuracy)3.2 精确率 (Precision)3.3 召回率…

RabbitMQ - 4 ( 22000 字 RabbitMQ 入门级教程 )

一&#xff1a; RabbitMQ 高级特性 前面主要讲解了 RabbitMQ 的概念和应用。RabbitMQ 实现了 AMQP 0-9-1 规范&#xff0c;并在此基础上进行了多项扩展。在 RabbitMQ 官方网站中详细介绍了其特性&#xff0c;我们将其中一些重要且常用的特性挑选出来进行讲解。 1.1 消息确认 …

图表控件Aspose.Diagram入门教程(7):在 C# 中删除 Visio 形状保护

是一个关于Microsoft Visio文件使用的类库。在ASP .NET网页应用程序、web服务器和Windows应用程序上的VSD和VDX 文件中&#xff0c;开发者仍然可以使用Aspose.Diagram。它还允许您在打开的文件中编辑图表的元素&#xff0c;然后以Visio和XML的格式导出。 ​ 在某些情况下&#…

Python学习(5):数据结构

1 列表 1.1 列表方法 列表数据类型支持很多方法&#xff0c;列表对象的所有方法所示如下&#xff1a; list.append(x)&#xff1a;在列表末尾添加一项。 类似于 a[len(a):] [x]。list.extend(iterable)&#xff1a;通过添加来自 iterable 的所有项来扩展列表。 类似于 a[len…

Qanything 2.0源码解析系列6 PDF解析逻辑

Qanything 2.0源码解析系列6: PDF解析逻辑 type: Post status: Published date: 2024/12/04 summary: 深入剖析Qanything是如何拆解PDF的,核心是pdf转markdown category: 技术分享 原文:www.feifeixu.top 😀 前言: 在前面的文章中探究了图片是怎么进行解析的,这篇文章对…

【OpenGL ES】GLSL基础语法

1 前言 本文将介绍 GLSL 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符&#xff08;in、out、inout&#xff09;、函数参数限定符等内容&#xff0c;另外提供了一个 include 工具&#xff0c;方便多文件管理 glsl 代码&a…

端口被占用

端口8080被占用 哈哈哈&#xff0c;我是因为后端项目跑错了&#xff0c;两个项目后端名称太像了&#xff1b; &#xff08;1&#xff09;netstat -aon | findstr 8080&#xff0c;找到占用8080端口的进程号&#xff0c;获取对应的进程号pid&#xff1b; &#xff08;2&#…

Spring中的设计模式

Spring中的设计模式 控制反转(IoC)和依赖注入(DI) IoC 是一个原则&#xff0c;而不是一个模式&#xff0c;以下模式&#xff08;但不限于&#xff09;实现了 IoC 原则。 **Spring IoC 容器就像是一个工厂一样&#xff0c;当我们需要创建一个对象的时候&#xff0c;只需要配置…

【2025 Rust学习 --- 09 特型和泛型】

特型和泛型 Rust 通过两个相关联的特性来支持多态&#xff1a;特型和泛型。许多 程序员熟悉这些概念&#xff0c;但 Rust 受到 Haskell 类型类&#xff08;typeclass&#xff09;的启发&#xff0c;采用 了一种全新的方式。 1、特型是 Rust 体系中的接口或抽象基类。乍一看&a…

【开源免费】基于Vue和SpringBoot的网上商城系统(附论文)

本文项目编号 T 129 &#xff0c;文末自助获取源码 \color{red}{T129&#xff0c;文末自助获取源码} T129&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

使用Locust对MySQL进行负载测试

1.安装环境 pip install locust mysql-connector-python 2.设置测试环境 打开MySQL服务 打开Navicat新建查询&#xff0c;输入SQL语句 3.编写locust脚本 load_mysql.py # codingutf-8 from locust import User, TaskSet, task, between import mysql.connector import ran…

MF248:复制工作表形状到Word并调整多形状位置

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

极品飞车6的游戏手柄设置

极品飞车&#xff0c;既可以用键盘来控制车辆的前进、后退、左转、右转、加速与减速&#xff0c;也可以使用游戏手柄来操作车辆的运行。需要注意的是&#xff0c;极品飞车虽然支持手柄&#xff0c;但是仅支持常见的北通、罗技还有部分Xbox系列的手柄&#xff0c;至于其他的PS4手…

2025元旦源码免费送

我们常常在当下感到时间慢&#xff0c;觉得未来遥远&#xff0c;但一旦回头看&#xff0c;时间已经悄然流逝。对于未来&#xff0c;尽管如此&#xff0c;也应该保持一种从容的态度&#xff0c;相信未来仍有许多可能性等待着我们。 免费获取源码。 更多内容敬请期待。如有需要可…