Unity笔记:C#基础(1)

news2024/7/6 17:45:59

杂项

虚函数

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字节对齐,大小为4
    int 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 == 16
    char x;
    int y;
    char v[6];
};
struct stu1 {
    // size == 32
    char a;
    struct stu2 b;
    double f;
};
struct stu2 {
    // size == 24
    char x;
    int y;
    double z;
    char v[6];
};
struct stu1 {
    // size == 48
    char 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.coloradmin.cn/o/1508498.html

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

相关文章

安装zabbix

部署Zabbix监控平台 部署一台Zabbix监控服务器&#xff0c;一台被监控主机&#xff0c;为进一步执行具体的监控任务做准备&#xff1a; 安装LNMP环境源码安装Zabbix安装监控端主机&#xff0c;修改基本配置初始化Zabbix监控Web页面修改PHP配置文件&#xff0c;满足Zabbix需求…

IP形象设计是什么设计?如何做?

随着市场竞争的激烈&#xff0c;越来越多的企业开始关注品牌形象的塑造和推广。在品牌形象中&#xff0c;知识产权形象设计是一个非常重要的方面。在智能和互联网的趋势下&#xff0c;未来的知识产权形象设计可能更加关注数字和社交网络。通过数字技术和社交媒体平台&#xff0…

vue本地实现生成二维码功能

首先导入依赖 npm install qrcode --save-dev然后是页面代码 <template><div><canvas ref"qrcodeCanvas"></canvas></div> </template><script> import QRCode from qrcode;export default {mounted() {this.generateQR…

(2023)从零开始用qemu搭建虚拟arm环境

用qemu搭建虚拟arm环境 引言安装版本 1. VMware ubuntu20.04 qemu安装2.安装交叉编译工具3.编译内核kernel4.u-boot编译5.制作根文件系统第一步&#xff1a;下载、编译和安装busybox第二步&#xff1a;形成根目录结构第三步&#xff1a;制作根文件系统镜像 测试HelloWorld应用…

Servlet API 详细讲解

Servlet API 详细讲解 文章目录 Servlet API 详细讲解1. HttpServlet2.HttpServletRequest服务器如何获取到 query string 和 body 的数据 &#xff1f;&#xff1f; 3.HttpServletResponse API就是一组类和方法的集合&#xff0c;servlet 中的 类是非常多的&#xff0c;咱们只…

Python实现图片(合并)转PDF

在日常的工作和学习过程当中,我相信很多人遇到过这样一个很普通的需求,就是将某一个图片转为PDF或者是将多个图片合并到一个PDF文件。但是,在苦苦搜寻一圈之后发现要么要下载软件,下载了还要注册,注册了还要VIP,甚至SVIP才能实现这样的需求! 今天,我带大家把这个功能打…

【学习笔记】VMware vSphere 6.7虚拟化入门

VMware vSphere 6.7虚拟化入门课程介绍 课程内容 1、VMware vSphere 6.7虚拟化入门课程介绍 2、ESXi6.7控制台设置 3、使用vSpkere Host client管理虚拟机 4、VMware EsXi基础操作 5、VMware Esxi存储管理 6、管理ESXi主机网络与虚拟机网络 7、安装配置vCenter Server Applia…

neo4j网页无法打开,启动一会儿后自动关闭,查看neo4j status显示Neo4j is not running.

目录 前情提要User limit of inotify watches reached无法访问此网站 前情提要 公司停电&#xff0c;服务器未能幸免&#xff0c;发现无法访问此网站&#xff0c;http://0.0.0.0:7474 在此之前都还好着 User limit of inotify watches reached (base) [rootlocalhost ~]# n…

利用MyRandom函数求一组指定个数的随机返回数

《VBA信息获取与处理》教程(版权10178984)是我推出第六套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。这部教程给大家讲解的内容有&#xff1a;跨应用程序信息获得、随机信息的利用、电子邮件的发送、VBA互…

搭建prometheus、grafana监控平台

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

【Qt】—— 信号与槽

目录 &#xff08;一&#xff09;信号和槽概述 1.1 信号的本质 1.2 槽的本质 &#xff08;二&#xff09;信号和槽的使用 2.1 信号和槽的连接 2.2 查看内置信号和槽 2.3 通过Qt Creator⽣成信号槽代码 &#xff08;三&#xff09;自定义信号和槽 3.1 基本语法 3.2 带参…

TCP多线程模型、IO模型(select、poll、epoll)

我要成为嵌入式高手之3月11日Linux高编第十九天&#xff01;&#xff01; ———————————————————————————— TCP并发模型 一、TCP多线程模型&#xff1a; 缺点&#xff1a;创建线程会带来资源开销&#xff0c;能够现的并发量比较有限 二、IO模型&…

产品设计 - 尼尔森交互设计原则

文章目录 前言1. 状态可见原则1.1 理论介绍1.2 实践应用 2. 环境贴切原则2.1 理论介绍2.2 实践应用 3. 用户可控原则3.1 理论介绍3.2 实践应用 4. 一致性原则4.1 理论介绍4.2 实践应用 5. 易用原则5.1 理论介绍5.2 实践应用 6. 灵活高效原则6.1 理论介绍6.2 实际应用 7. 优美简…

opengl 学习(五)-----变换

变换 分类向量向量与标量运算向量取反向量加减向量相乘点乘叉乘 矩阵矩阵的加减矩阵的数乘矩阵相乘 矩阵与向量相乘单位矩阵缩放位移 旋转GLMdemo效果解析教程 分类 OpenGL C 向量 下面有一个解释的非常好的理解: 向量有一个方向(Direction)和大小(Magnitude&#xff0c;也叫…

网络编程(3/6)

使用C语言完成数据库的增删改 #include<myhead.h> int do_add(sqlite3 *ppDb) {int numb;char name[50];int salary;printf("请输入员工信息&#xff1a;工号、姓名、薪水\n");scanf("%d %s %d",&numb,name,&salary);char sql[128];char *e…

关于遗传力常见的误解

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

Ping工作原理

文章目录 目的ping网络协议 OSIICMP什么是ICMP作用功能报文类型查询报文类型差错报文类型ICMP 在 IPv4 和 IPv6 的封装ICMP 在 IPv4 协议中的封装ICMP 在 IPv6 协议中的封装ICMP 头部日常ping 排除步骤ping 查询报文使用code扩展目的 本文主要是梳理ping的工作原理- 揭开 ICMP…

FPGA AXI4总线操作教程

AXI&#xff08;Advanced Extensible Interface&#xff09;总线是一种高性能、低延迟的片上系统&#xff08;SoC&#xff09;接口标准&#xff0c;广泛应用于现代数字系统设计中。它允许不同的硬件组件以高效、可靠的方式进行数据传输和控制。本教程将介绍AXI总线的基本操作和…

C++面向对象..

1.面向对象的常见知识点 类、 对象、 成员变量(属性)、成员函数(方法)、 封装、继承、多态 2.类 在C中可以通过struct、class定义一个类 struct和class的区别&#xff1a; struct的默认权限是public(在C语言中struct内部是不可以定义函数的) 而class的默认权限是private(该权…

上位机图像处理和嵌入式模块部署(qmacvisual旋转和镜像)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 旋转和镜像是图像处理中经常遇到的一个情况。很多时候&#xff0c;摄像头面对物体进行拍摄&#xff0c;未必是正对着进行拍摄的&#xff0c;这个时…