Unity笔记:C#基础(1)

杂项

虚函数

CSDN - C++虚函数详解
cnblog - C#中的虚函数virtual

常量池与new

在C#中,string是不可变的,这意味着对string对象的操作通常会返回一个新的string对象,而不会修改原始的string对象。因此,几乎所有涉及更改string内容的方法都会返回一个新的string对象。
String s = new String("xyz")在内存中产生了多少份字符串?2个。

  1. “xyz” 字符串的常量池中的字符串对象
  2. new出来的新字符串

这种方式创建的字符串对象不会被放入常量池中,正确的操作是下面这样

string s = "xyz";

在C#中,常量池(intern pool)通常是被放置在堆中。而Substring这类操作均不会改变原来的字符串,而是创建新的。

拆箱与装箱

拆箱(Unboxing)和装箱(Boxing)是与值类型(Value Type)和引用类型(Reference Type)之间的转换相关的两个概念。

  1. 装箱(Boxing)
    • 装箱是指将值类型转换为引用类型的过程。在装箱中,值类型的实例被封装在一个对象中,并在堆上分配内存空间。
    • 例如,将一个整数值装箱为 object 类型的实例,或者将一个结构体实例装箱为 System.ValueType 类型的实例。
  2. 拆箱(Unboxing)
    • 拆箱是指将引用类型转换为值类型的过程。在拆箱中,封装在对象中的值类型实例被提取出来,放入到一个新的值类型变量中。
    • 例如,将一个装箱的整数对象拆箱为一个整数值,或者将一个装箱的结构体对象拆箱为原始的结构体实例。

装箱和拆箱操作可能会引起性能开销,因为它们涉及到数据的复制和内存分配。因此,在编写高性能的代码时应该谨慎使用。

注1:在装箱过程中,值类型的数据会被复制到堆上新分配的内存空间中,而引用会指向这个新分配的内存空间,因此装箱后的引用指向的是堆上的对象。

注2:当创建一个新的结构体时,编译器会隐式地为它添加继承自 System.ValueType 的基类,并在必要时自动实现一些与值类型相关的功能,比如装箱、拆箱等。


List会发生拆装箱吗?会,List<object>就会发生

List<object> objectList = new List<object>();
objectList.Add(20); // 添加一个整数(值类型)
objectList.Add("World"); // 添加一个字符串(引用类型)

可以使用is关键字或as关键字来检查List<object>中的某个元素的类型。从 List<object> 中取出元素时,元素的类型会被视为 object 类型,因此任何按值类型进行的操作都需要显式手动转换类型。

C#关键字

C# 中的 sealed 关键字类似于 Java 中的 final 关键字,用于类(防止继承)或方法(防止重写)

readonlyconst区别在于const是编译时常量,而readonly是运行时常量:

  1. const关键字用于声明常量,常量在声明时必须进行初始化,并且一旦初始化后,其值将无法更改。const变量在编译时会被直接替换为其值,因此它们的值必须在编译时就能确定。
  2. readonly关键字用于声明只读字段,只读字段可以在声明时或构造函数中进行初始化,一旦初始化后,其值将无法更改。与const不同,readonly字段的值是在运行时确定的,因此可以用于在构造函数中初始化。

partial关键字

partial关键字用于指示一个类、接口、结构体或方法是“部分定义”的。这意味着该类、接口、结构体或方法的定义可以分散在多个文件中。

// File1.cs
partial class MyClass
{public void Method1(){Console.WriteLine("Method1");}
}// File2.cs
partial class MyClass
{public void Method2(){Console.WriteLine("Method2");}
}

不要试图在不同文件中重复定义某些方法或者变量,会报错。

System.Object

在C#中,所有引用类型的基类System.Object,该类实现了几个方法

在这里插入图片描述

Try

try
{// 可能会抛出异常的代码块
}
catch (ExceptionType1 ex)
{// 处理特定类型的异常
}
catch (ExceptionType2 ex)
{// 处理另一种类型的异常
}
finally
{// 无论是否发生异常,都会执行的代码块
}

如果catch后面没有括号里的条件,那就会捕获 try 块中抛出的任何类型的异常。自定义异常需要创建一个继承自 System.Exception 类的新类

using System;// 定义自定义异常类
public class MyCustomException : Exception
{// 可以添加自定义的构造函数和属性public MyCustomException(string message) : base(message){}
}public class Program
{public static void Main(string[] args){try{// 在适当的情况下,抛出自定义异常throw new MyCustomException("This is a custom exception.");}catch (MyCustomException ex){// 捕获并处理自定义异常Console.WriteLine("Custom Exception Caught: " + ex.Message);}catch (Exception ex){// 捕获其他类型的异常Console.WriteLine("Exception Caught: " + ex.Message);}}
}

注意在C#中,构造函数的调用顺序是由派生类向基类的方向,所以是派生类先调用基类构造函数执行,执行完才执行本类的,所以执行顺序是基类到派生类。

对于C#中的多重继承,基类构造函数的执行顺序是由派生类中基类声明的顺序决定的,一般就是这句话:

public class DerivedClass : Base1, Base2
{// ... 
}

例如下题结果为6

int x = 0;
try
{ throw new Exception(); }
catch
{ x += 1; }
finally
{ x += 2; }
x += 3;

观察者模式、委托与Unity

CSDN - Unity中关于委托与事件的使用及区别
c# 事件和委托,再也不忘了

事件是函数的容器,类似C的函数指针但不太一样。声明事件时需要先声明一个委托类型

  • 委托通常用于实现回调函数、事件处理等场景,它可以直接被调用。
  • 事件通常用于实现发布-订阅模式,它只能在声明类的内部触发,外部无法直接调用。

一般在OnEnable()OnDisable()中注册和移除事件的订阅而非Start(),这样不会在计算机内存中留下任何无法访问的Object。事件的调用如同调用函数一般,但是在那之前需要测试事件是否为 null,只有当任何类中没有函数订阅该事件时,该事件才会为 null

委托的本质可以看作是观察者模式的一种实现方式。委托的核心是事件,用到事件的地方就可以使用委托,例如UI交互;捡到某个物品时触发一个事件,该事件将为玩家提供升级等效果;或者触发了碰撞器能够打开门;还有就是状态管理。

实际上物体的碰撞事件通常是通过委托来实现的,如OnCollisionEnterOnCollisionStayOnCollisionExit等方法

C#与多继承

C#不直接支持多继承,一般使用接口实现类似效果。

内存对齐

CSDN - 【C/C++】内存对齐(超详细,看这一篇就够了)
有必要注意的是,这篇的例5讲的不太对,图也错了,我把我的理解放在了下面小节“结构体嵌套的对齐”

使用 #pragma pack(n) 指令会将结构体的对齐方式设置为 n 字节的整数倍,其中n是2的次方。例如一个大小为13的变量通常会对齐到16。

使用 #pragma pack()则取消强制对齐,恢复默认

注意:填充的缝隙也算在结构体/类的大小内,下面是个例子:

// sizeof(Base) == 8
// int4字节,bool按最大对齐
// 这里的策略是编译器在对结构体进行对齐时,按照结构体中最大的成员大小进行对齐。
class Base { int a; bool b; };

常见的对齐策略包括:

  1. 最严格对齐原则(Strictest Alignment):按照结构体中任何成员的要求,选择最严格的对齐方式。这意味着所有成员都按照自身的对齐要求进行对齐。
  2. 平均对齐原则(Average Alignment):根据结构体中所有成员的对齐要求的平均值进行对齐。这种策略可能会导致一些成员需要额外的填充来满足对齐要求。
  3. 特定对齐方式(Specified Alignment):有些编译器允许在结构体定义中指定对齐方式,例如使用 #pragma pack 或者 __attribute__((packed))。在这种情况下,结构体的对齐将根据指定的方式进行,而不是根据成员的大小。
  4. 默认对齐方式(Default Alignment):一些编译器有默认的对齐方式,可能会在不指定特定对齐方式的情况下应用。这通常会是一个合理的默认值,可以满足大多数情况下的性能和内存使用需求。

基本原则

知乎 - C/C++中内存对齐问题的一些理解
CSDN - 计算结构体大小(内存对齐原则)struct、union、class

就原则来讲,第二篇CSDN的博客是很详细的(其实很多东西我在Cppreference上没查到)

  1. 数据成员对齐规则,结构体(struct)(或联合(union))的数据成员,第一个数据成员存放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员(只要该成员有子成员,比如数组、结构体等)大小的整数倍开始(如:int 在 64bit 目标平台下占用 4Byte,则要从4的整数倍地址开始存储)
  2. 结构体的总大小,即sizeof的结果,必须是其内部最大成员长度(即前面内存对齐指令中提到的有效值)的整数倍,不足的要补齐(似乎union也需要满足这点)如果结构体A作为结构体B的成员,B的对齐大小总是按照#pragma pack(n)进行,其中n = max{A最大元素, B最大元素}

声明顺序的影响

struct st1 {char a[5];char b[3];int c;
};
struct st2 {char a[5];int c;char b[3];
};
// st1 == 12
// st2 == 16

这个归根到底是因为:相同类型的成员会连续存储在一起,不会因为对齐要求而产生间隔。数组内的n个成员视作相同类型的n个成员

struct st1 {int a[1];double p;char b[3];
};
struct st2 {char b[3];int a[1];double p;
};
// st1 == 24
// st2 == 16

类的对齐(虚函数与空类)

  1. 按照结构体对齐原则
  2. class含有成员变量和成员函数:计算大小的时候只与成员变量有关。与成员函数和静态成员无关,即普通成员函数、静态成员函数、静态成员变量。对类的大小没有影响。
  3. 虚函数对类的大小有影响,因为虚表指针的影响。在32位系统占4个字节,64位系统占8个字节。
  4. 多个虚函数也只算一个的影响。

在 C++ 中,对于空类(没有任何成员),其大小通常是 1 字节,这是因为 C++ 编译器会确保每个实例都有一个唯一的地址。

class Base{};
class Drived
{Base a;  // 类内没东西,按4字节对齐,大小为4int b;  // 一个int为4字节
};
cout << sizeof(Drived) << endl;  // 输出8

可以尝试修改Base类再输出:

// 这种情况输出也是8
class Base { int a; };
// 这种情况输出是12
class Base { int a; int b; };

在C#中,类的实例化在内存中会被对齐。即使一个类没有任何成员,它也会在内存中被对齐,其大小通常是一个指针的大小,因为每个类实例在CLR(Common Language Runtime)中都会关联一个类型对象指针。

枚举的对齐

枚举类型的对齐与其底层类型一致,在C++一般是int,但是C++11可以指定为其他合法的整数类型,如unsigned intcharshort,用法如下:

enum class MyEnum : underlying_type {VALUE1,VALUE2,VALUE3
};

例子如下:

enum DAY {MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
// C++11新特性允许显式地指定枚举的底层类型
enum class DAY1 : char {MON = 'a', TUE, WED, THU, FRI, SAT, SUN
};
struct st1 {DAY b;
};
struct st2 {DAY1 b;
};
cout << sizeof(st1) << endl;	// 4
cout << sizeof(st2) << endl;	// 1

更复杂的情况,即枚举与其他的组合,就把enum当做某种整数类型计算即可。

union的大小

联合体和结构体一样,存在内存对齐

Cppreference:联合体只大到足以保有其最大成员(亦可能添加额外的尾随填充字节)。
上面的某博客:当联合体中有数组时,一方面要保证空间能够存储这个数组的大小,另一方面要保证最终的结果是最大数据类型的整数倍。

union MyUnion {char a[10];int b;double c;
};
// 大小输出是16,而不是10

如果加上#pragma pack(1)就是输出10了

不知道算不算参考的参考:MSDN - x64 ABI 约定概述

结构体嵌套的对齐

结合这两段代码对比:

struct stu2 {// size == 16char x;int y;char v[6];
};
struct stu1 {// size == 32char a;struct stu2 b;double f;
};
struct stu2 {// size == 24char x;int y;double z;char v[6];
};
struct stu1 {// size == 48char a;struct stu2 b;int c;int d;int f;
};
// 如果stu1去掉一个int,大小为40,去掉2个int大小也为40

换句话说,结构体嵌套的情况下,在默认对齐方式的情况下,总是n的整数倍,其中n = max{A中最大元素, B中最大元素}

c#的sizeof

C#无法直接使用 sizeof 操作符来获取结构体的大小

在 C# 中,sizeof 操作符用于获取未托管类型或静态成员的大小,但它不能用于获取托管类型(如结构体或类)的大小。这是因为托管类型的大小在编译时并不总是已知的,而是在运行时由 CLR (Common Language Runtime) 动态确定的。

要获取托管类型(如结构体)的大小,通常可以使用 System.Runtime.InteropServices.Marshal.SizeOf 方法,该方法在运行时动态计算类型的大小。

C#结构体布局

先看上一小节内存对齐

CSDN - C#结构体内存对齐

CSDN - C#-StructLayoutAttribute(结构体布局)

在 C# 中,结构体的布局方式可以通过 StructLayoutAttribute 特性来控制,而 LayoutKind 枚举类型用于指定这种布局方式的具体类型。

  1. Auto:自动布局。编译器根据目标平台和类型成员的排列顺序来确定结构体的布局方式
  2. Sequential:顺序布局。结构体的成员按照声明的顺序依次排列,不考虑对齐和填充。
  3. Explicit:显式布局。需要手动指定每个成员的偏移量,可以使用 FieldOffsetAttribute 特性来指定偏移量。
[StructLayout(LayoutKind.Sequential)]
public struct Point
{public int x;public int y;
}[StructLayout(LayoutKind.Explicit)]
public struct Rect
{[FieldOffset(0)] public int left;[FieldOffset(4)] public int top;[FieldOffset(8)] public int right;[FieldOffset(12)] public int bottom;
}

Golang选手看这个

如果是Golang选手就看这篇拿Golang讲的:CSDN - 详解内存对齐

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis最新版本在Windows系统上的安装

一、说明 这次安装操作主要是根据redis官网说明&#xff0c;一步步安装下来的&#xff0c;英语比较好的同学&#xff0c;可以直接看文章底部的超链接1&#xff0c;跳到官网按步操作即可。 目前redis的最新稳定版本为redis7.2。 二、Windows环境改造 Redis在Windows上不被官方…

学校里的软件测试专业技能到底怎样,为什么应届生都很难找工作?

大家好&#xff0c;今天和以为在学校教软件测试的老师聊了天&#xff0c;主要聊的主题是-为什么现在大专生就业这么困难。主要总结以下几点&#xff01; 一是受当下大环境的影响&#xff08;比如疫情、俄乌、单边主义等&#xff09;&#xff1b;二是今年的应届生实在太多&…

智慧城市的前景:数字孪生技术在智慧城市中的应用前景

目录 一、引言 二、数字孪生技术及其在智慧城市中的应用概述 三、数字孪生技术在智慧城市中的应用前景 1、城市规划与仿真模拟 2、智能交通与出行服务 3、智慧环保与可持续发展 4、智慧公共服务与社会治理 5、智慧能源与绿色建筑 四、数字孪生技术在智慧城市中的挑战与…

ACL的应用与ENSP配置

目录 ACL的定义 ACL的功能 访问控制列表的调用方向 访问控制列表类型 1、标准访问控制列表 2、扩展访问控制列表 访问控制列表的处理原则 ENSP调用命令 经典案例 tip&#xff1a;RFC 1918私有地址空间 ACL的定义 ACL&#xff0c;中文名称是“访问控制列表”&#xff…

PID控制器组(完整SCL代码)

PID控制器组不是什么新概念,是在PID控制器的基础上,利用面向对象的思想对对象进行封装 批量实例化。 1、增量式PID https://rxxw-control.blog.csdn.net/article/details/124363197https://rxxw-control.blog.csdn.net/article/details/1243631972、完全增量式PID https:/…

三井住友保险中国区信息技术部负责人陈婧,将出席“ISIG-RPA超级自动化产业发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;RPA中国、AIGC开放社区、LowCode低码时代&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索R…

蓝桥杯[OJ 2928]分糖果-CPP(贪心、字典序)

目录 一、题目描述&#xff1a; 二、整体思路 (一)字典序比较规则 (二)正确理解题意 &#xff08;三&#xff09;分类讨论 三、代码 一、题目描述&#xff1a; 二、整体思路 (一)字典序比较规则 首先要知道字典序是怎么比较大小的&#xff0c;简单来说按以下次序进行比较&am…

掌握Mongodb,看完这篇文章就够了

目录 1.概念 2.操作 2.1数据库操作 2.2集合操作 2.3数据操作 3.查询 4.常用技术 5.python与MongoDB 1.概念 MongoDB是一种非关系型数据库&#xff08;NoSQL&#xff09;&#xff0c;它以灵活的文档存储格式&#xff08;BSON&#xff09;和强大的查询能…

Python网站的搭建和html基础

1.Python网站代码及讲解 一般我们搭建小型的网站就用flask库就行了。 &#xff08;1&#xff09;安装flask库 安装完python后&#xff0c;按住windows徽标键和r,弹出“运行”&#xff0c;在里面输入cmd。 回车打开&#xff0c;输入“pip install flask”。 &#xff08;2&am…

Python爬虫实战第三例【三】【上】

零.实现目标 爬取视频网站视频 视频网站你们随意&#xff0c;在这里我选择飞某速&#xff08;狗头保命&#xff09;。 例如&#xff0c;作者上半年看过的“铃芽之旅”&#xff0c;突然想看了&#xff0c;但是在正版网站看要VIP&#xff0c;在盗版网站看又太卡了&#xff0c;…

POS 之 最终确定性

Gasper Casper 是一种能将特定区块更新为 最终确定 状态的机制&#xff0c;使网络的新加入者确信他们正在同步规范链。当区块链出现多个分叉时&#xff0c;分叉选择算法使用累计投票来确保节点可以轻松选择正确的分叉。 最终确定性 最终确定性是某些区块的属性&#xff0c;意味…

【Appium问题】每次启动appium都会安装一次uiautomator

问题 每次启动appium&#xff0c;都需要安装一次uiautomator2比较麻烦 解决 在配置文件capabilities 中增加参数skipServerInstallationTrue

如何实现无公网ip环境使用vscode远程ssh内网Linux系统写代码

文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…

Java语法学习六之继承和多态(重要)

继承 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需要考虑。 比如&#x…

3.11_C++_day1_作业

作业要求&#xff1a; 程序代码&#xff1a; #include <iostream> #include <string.h>using namespace std;int main() {int a0,b0,c0,d0,e0;//分别记录字符串中的大写&#xff0c;小写&#xff0c;数字&#xff0c;空格&#xff0c;其他字符个数string str;cha…

五、OpenAI实战之Assistants API

在8线小城的革委会办公室里&#xff0c;黑8和革委会主任的对话再次展开。 黑8&#xff1a;主任&#xff0c;您知道吗&#xff1f;除了OpenAI API&#xff0c;现在还有一项新的技术叫做Assistants API&#xff0c;它可以帮助我们更好地进行对话和沟通。 主任&#xff1a;Assis…

Milvus 向量数据库实践 - 1

假定你已经安装了docker、docker-compose 环境 参考的文档如下&#xff1a; Milvus技术探究 - 知乎 MilvusClient() - Pymilvus v2.3.x for Milvus 一文带你入门向量数据库milvus 一、在docker上安装单机模式milvus数据库 1、 进入milvus官网&#xff1a; Install Milvus Stand…

关于遗传力常见的误解

大家好&#xff0c;我是邓飞&#xff0c;今天看了一篇非常好的文章&#xff0c;介绍了遗传力相关概念和计算方法&#xff0c;里面提到了常见的误解&#xff0c;这里汇总一下。 文献链接&#xff1a;https://excellenceinbreeding.org/sites/default/files/manual/EiB-M2_Herit…

数据结构---复杂度(2)

1.斐波那契数列的时间复杂度问题 每一行分别是2^0---2^1---2^2-----2^3-------------------------------------------2^(n-2) 利用错位相减法&#xff0c;可以得到结果是&#xff0c;2^(n-1)-1,其实还是要减去右下角的灰色部分&#xff0c;我们可以拿简单的数字进行举例子&…

神经网络实战前言(补充)

深度学习 深度学习是特殊的机器学习&#xff0c;使用复杂的、多层神经网络进行学习。深度神经网络&#xff08;DNN&#xff09;&#xff0c;每层学习的信息的复杂度是不断增加的。例如面部识别&#xff0c;第一层识别眼睛、第二层识别鼻子&#xff0c;直到所有的面部特征识别完…