【唐老狮】Unity系列之C#四部曲—C#基础

复杂数据类型(枚举、数组、结构体)
值和引用的区别
函数相关
排序初探
----------------------------------------------------------
复杂数据类型概述
- 枚举:整形常量的集合,可以自定义
- 数组:任意变量类型顺序存储的数据
- 结构体:任意变量的数据集合,可以自定义
----------------------------------------------------------
枚举
枚举:被命名的整形常量的集合
- 申明枚举:创建一个自定义的枚举类型
- 申明枚举变量:使用申明的自定义枚举类型,创建一个枚举变量
申明枚举语法:以E或者E_开头
格式:
enum E_自定义枚举名
{
自定义枚举项名字,// 默认值是0
自定义枚举项名字,// 变成1
}
示例:
enum E_PlayerType
{
Main,// 默认值是0
Other,// 自动变成1
Other1 = 100, // 变成100
Other2 = // 自动变成101
}
--------------------
- 在namespace语句块中申明
- 也可以在class语句块中和结构体中
- 但不能在函数语句块中申明,包括循环和分支语句中
--------------------
申明枚举变量:
E_PlayerType playerType = E_PlayerType.Main;
配合【贯穿】使用
--------------------
- 枚举和int互转:强制类型转换
int i = (int)playerType;
- 枚举和string相互转换:
string str = playerType.ToString() ;// 将名字转换成string
格式: (强制转换类型)Enum.Parse(typeof(转换的类型), 用于转换的对应的枚举项)
备注:转换完毕后,是一个通用类型,我们需要用括号强转成我们想要的目标枚举类型
playerType = (E_PlayerType)Enum.Parse(typeof(E_PlayerType), 'Other') ;
--------------------
在游戏开发中,对象很多时候有很多状态
枚举可以帮我们分清状态的含义
----------------------------------------------------------
一维数组
储存相同类型数据的集合,一维数组简称为数组
- 1.变量类型[] 数组名;
只是申明,没有初始化
变量类型:int 等 c#规定的变量类型
- 2.变量类型[] 数组名 = new 变量类型[数组的长度];
规定数组的长度,但没有指定数组内容
默认值是0,示例:int[] arr2 = new int[5];
- 3.变量类型[] 数组名 = new 变量类型[数组的长度]{内容1,内容2,..};
指定数组内容
示例:int[] arr2 = new int[5]{1,2,3,4,5};
- 4.变量类型[] 数组名 = new 变量类型[]{内容1,内容2,..};
不写长度,只写内容,内容长度决定数组长度
- 5.变量类型[] 数组名 = {内容1,内容2,..};
与上面同理
注意:申明的数组是什么类型,后面内容必须是相同数据类型的数据
- 数组长度:数组名. Length
- 获取数组中的元素:索引从0开始,数组名[索引]
- 修改数组中的元素:array[0] = 99
- 遍历元素:for (int i = 0; i < array.Length; i++){}
- 增加数组元素:数组初始化后,不能直接添加新的元素;只能拷贝到一个更大的数组中,然后将新数组的地址赋值给原数组
- 同理,也不能删除元素
- 查找数组中的元素:遍历依次查找
用来批量储存游戏中的同一类型的对象的容器,比如所有怪物
----------------------------------------------------------
二维数组
两个下标来确定元素的数组,在内存中以【行优先】方式存储
- 变量类型[ , ] 变量名;
- 变量类型[ , ] 变量名 = new 变量类型 [行数,列数];
- 变量类型[ , ] 变量名 = new 变量类型 [行数,列数]{ {0行内容}, {1行内容}};
- 变量类型[ , ] 变量名 = new 变量类型 [,] {{0行内容}, {1行内容}};
- 变量类型[ , ] 变量名 = {{0行内容}, {1行内容}};
- 数组名.GetLength(0); 行数
- 数组名.GetLength(1); 列数
- 元素:array[0, 1]
----------------------------------------------------------
交错数组
数组的数组,每个维度的数量可以不同
每个行储存一个一维数组
--------------------
int[][] arr;
int[][] arr = new int[行数][];
int[][] arr = new int[行数][]{ new int[] {1, 2, 3},
new int[] {1, 2},
new int[] {1}, };
int[][] arr = new int[][]{ ... };
int[][] arr = { ... };
--------------------
- 数组名.GetLength(0); 行数
- 数组名[ 某个行号 ].Length;
- 元素:array[0][1] 对比二维数组,不是array[0, 1]
----------------------------------------------------------
值类型和引用类型
变量类型:
- 无符号整形:byte,ushort,uint,ulong
- 有符号整型: sbyte,short,int,long
- 浮点数: float,double,decimal
- 特殊类型:char,string,bool
- 复杂类型:enum,数组
引用类型:string、数组、类
值类型:其他,结构体
--------------------
int a = 10;
int[] arr = new int[]{ 1,2,3,4}
int b = a;
int[] arr2 = arr;
值类型在相互赋值时,把内容拷贝给了对方,它变我不变
引用类型的相互赋值,是让两者指向同一个内存空间,它变我也变
值类型 和 引用类型 存储在内存区域 是不同的,存储方式是不同的
值类型存储在栈空间(系统分配的空间,自动回收,小而快)
引用类型存储在堆空间(手动申请和释放,大而慢)
引用类型储存【在栈中】储存的只是一个【指向堆中】的地址
--------------------
int[] arr = new int[]{ 1,2,3,4};
int[] arr2 = arr1;
arr2 = new int[] {99,2,3,4};
arr2在地址中开辟新的空间,与arr1没有关系了
----------------------------------------------------------
特殊引用类型string
string str1 = '123';
string str2 = str1;
str2 = "321" // 但str1不会变,c#对string进行了特殊处理
重新赋值时,会在堆中重新分配一个空间
但频繁重新赋值会产生内存垃圾
--------------------
通过断点调试 在监视窗口中查看 内存信息
----------------------------------------------------------
函数基础
本质:具有名称的代码块
作用:抽象行为
选中函数,f11转到函数内部
--------------------
class语句块/struct语句块中
--------------------
static 返回类型 函数名 (参数类型 参数名1,参数类型 参数名2,...,)
{
函数语句块
return 返回值;// 如果函数开头有返回类型时才返回
}
注意事项:
- static不是必须的
- 返回类型是 void 时,表示函数没有返回值
- 返回类型可以写任意变量类型
- 函数命名用帕斯卡命名法命名
- 参数不是必须的,参数类型可以是任意类型
- 返回值不是void时,必须通过return返回对应类型内容
--------------------
函数返回值只能是一个数据类型,只能是一个内容
--------------------
即使没有返回值,也可以使用return来终止函数运行
----------------------------------------------------------
ref和out
在函数内部改变外部传入的内容,里面变了,外面也要变
值类型和引用类型作为参数的传参过程
如果函数接收一个数组,并在函数内部对接受的数组重新赋予了一个新的数组地址,那么这种改变是无法施加到传入的数组上的。
--------------------
目标:当传入的值类型参数在内部修改时,或者引用类型参数在内部重新申明时,外部值会发生相应的变化(变化能传递出函数)
ref/out:绑定这些参数在栈中的地址
--------------------
ref传入的变量必须初始化,但out不用
out传入的变量必须在内部赋值,ref不用
// ref传入的变量必须初始化,在内部可改可不该
// out传入的变量不用初始化,在内部,必须修改该值
----------------------------------------------------------
变长参数和参数默认值
--------------------
关键字:params
static int Sum(params int[] arr){}
注意点:
- params关键字后面必为数组
- 数组类型可以是任意类型
- 函数参数可以有别的参数和params关键字修饰的参数
- 函数参数中只能最多出现一个params关键字,并且一定是在最后一组参数,前面可以有n个其他参数
--------------------
有参数默认值的参数被称为可选参数
作用是当调用函数时可以不传入参数,不传就会使用默认值作为参数的值
注意点:
- 支持多参数默认值,每个参数都可以有默认值
- 如果要混用可选参数,必须写在普通参数后面
----------------------------------------------------------
函数重载
同一语句块中(class 或 struct)函数名相同,参数的数量不同
或者:参数的数量相同,但参数的类型或者顺序不同
作用:命名一组功能相似的函数,减少函数名的数量,避免命名空间的污染
--------------------
注意点:
- 重载和返回值类型无关,之和参数类型、个数、顺序有关
- 调用时,程序会自己根据传入的参数类型判断使用哪一个重载
ref和out可以看作新的参数类型
但ref和out不能同时修饰
----------------------------------------------------------
递归函数
让函数自己调用自己
正确的递归函数:
- 终止条件,且确保终止条件可以被触发
- 递归代码
static void Fun(int a){ //打印从0到10
Console.WriteLine(a);
if(a == 10){ return }
Fun(a+1);
}
----------------------------------------------------------
结构体
- 自定义变量类型,类似枚举需要自己定义
- 是数据和函数的集合
- 在结构体中,可以申明各种变量和方法
- 用来表现存在关系的数据集集合
--------------------
- 一般写在namespace语句块中
- 关键字:struct
struct 自定义结构体名
{
// 第一部分:变量
//第二部分:构造函数(可选)
//第三部分:函数
}
注意点:结构体名字用帕斯卡命名法
--------------------
变量:这个数据结构的属性
- 结构体申明的变量,不能直接初始化
- 变量类型可以写任意类型,包括结构体,但不能申明和自己一样的结构体对象
函数方法:表现这个数据结构的行为
- 在结构体中的方法,目前不需要加static关键字
- 在函数中,可以直接使用结构体内部申明的变量
--------------------
--------------------
修饰结构体中的变量和方法,是否能被外部使用
- public:公共的,可以被外部访问,被修改,外部调用形式:【结构体实例.变量 = value】
- private:私有的,默认类型,只能在定义结构体的代码内部被调用
--------------------
概念:
- 没有返回值
- 函数名必须和结构体名相同
- 必须有参数
- 如果申明了构造函数,那么必须在其中对所有变量数据初始化
相当于python的self关键字,用以区分是否是本结构体的变量或函数
每个结构体默认自带一个没有任何参数的构造函数;
手动写构造函数并加入参数时,相当于重载构造函数;
--------------------
概念:变量和函数的集合,用来表示特定的数据集合
访问修饰符:public和private
构造函数:初始化结构体对象
this修饰符:区分所属关系
注意:
- 结构体中申明的变量不能初始化,只能在外部或函数中初始化
- 在结构体中申明的函数不用加static
----------------------------------------------------------
冒泡排序
两两相邻,不停比较,不停交换,比较n轮。
冒泡排序是一种简单的排序算法,它重复地遍历待排序的序列,一次比较两个元素,如果它们的顺序错误就交换过来。遍历所有元素的过程会重复进行,直到没有再需要交换,也就是该序列已经排序完成。冒泡排序的名称来自于较大的元素会经由交换慢慢“浮”到数列的顶端,就像泡泡一样。
public void BubbleSort(int[] arr)
{
int n = arr.Length;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{// Swap arr[j] and arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
如果遍历过程发现一次交换都没有进行过,那么程序就可以提前结束了
public void BubbleSort(int[] arr)
{
int n = arr.Length;
bool isSort = false;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{// Swap arr[j] and arr[j + 1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
if(!isSort){
break;
}
}
}
}
--------------------
选择排序