课程目录
第 1 章: 概述
第 2 章: 复杂数据类型
- 第 1 节: 枚举
- 2-1-1: 枚举知识点
- 2-1-2: 枚举练习题
- 第 2 节: 一维数组
- 2-2-1: 数组知识点
- 2-2-2: 数组练习题 1
- 2-2-3: 数组练习题 3
- 第 3 节: 二维数组
- 2-3-1: 二维数组知识点
- 2-3-2: 二维数组练习题
- 第 4 节: 交错数组
第 3 章: 值类型和引用类型
- 第 1 节: 使用和存储上的区别
- 3-1-1: 使用和存储上的区别知识点
- 3-1-2: 使用和存储上的区别练习题
- 第 2 节: 特殊的引用类型 string
- 3-2-1: 特殊的引用类型 string 知识点
- 3-2-2: 引用类型练习题
第 4 章: 函数
- 第 1 节: 函数基础
- 4-1-1: 函数知识点
- 4-1-2: 函数练习题
- 第 2 节: ref 和 out
- 4-2-1: ref 和 out 知识点
- 4-2-2: ref 和 out 练习题
- 第 3 节: 变长参数和参数默认值
- 4-3-1: 变长参数和参数默认值知识点
- 4-3-2: 变长参数和参数默认值练习题
- 第 4 节: 函数重载
- 4-4-1: 函数重载知识点
- 4-4-2: 函数重载练习题
- 第 5 节: 递归函数
- 4-5-1: 递归函数知识点
- 4-5-2: 递归函数练习题
第 5 章: 复杂数据类型——结构体
第 6 章: 排序初探
- 第 1 节: 冒泡排序
- 6-1-1: 冒泡排序知识点
- 6-1-2: 冒泡排序练习题
- 第 2 节: 选择排序
- 6-2-1: 选择排序知识点
- 6-2-2: 选择排序练习题
第 7 章: C#基础知识点总结
实践项目目录
- 第 1 章: 需求分析
- 第 2 章: 项目实践
- 第 1 节: 控制台基础设置
- 第 2 节: 多个场景切换
- 第 3 节: 开始场景逻辑实现
- 第 4 节: 游戏场景逻辑实现
- 2-4-1: 不变的红墙
- 2-4-2: 格子结构体和枚举
- 2-4-3: 地图结构体
- 2-4-4: 玩家结构体和枚举
- 2-4-5: 扫雷子
- 第 5 节: 结束场景逻辑实现
- 第 3 章: C#基础实践总结
枚举
语法
枚举就相当于自己定于一个变量。
在NameSpace 和calls 和struct 语句块中声明。
在函数 语句中使用。
声明枚举: 创建一个自定义的枚举类型。
声明枚举变量: 使用声明自定义的枚举类型 创建一个枚举变量。
1 2 3 4 5 6 7
| enum E_自定义枚举名 { 自定义枚举项名字1, 自定义枚举项名字2, 自定义枚举项名字3, 自定义枚举项名字4, }
|
枚举和 switch:
他们通常配合使用,像 unity 的标签,能用于判断状态 和类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| using System;
namespace Lesson_1 { enum E_MonsterType { Normal, Boss, }
enum E_playerType { Main, Other, }
class Program { static void Main(string[] args) { E_playerType PlayerType = E_playerType.Main; switch (PlayerType) { case E_playerType.Main: Console.WriteLine("主玩家逻辑"); break;
case E_playerType.Other: Console.WriteLine("其他玩家逻辑"); break;
default: Console.WriteLine("无法判断玩家"); break; } } } }
|
枚举类型的转换
1 2 3 4 5 6 7
| int i = (int)PlayerType; Console.WriteLine(i);
string j = PlayerType.ToString();
PlayerType = (E_playerType)Enum.Parse(typeof(E_playerType), "Other");
|
数组
一维数组
数组的声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
int[] arr1;
int[] arr2 = new int[5];
int[] arr3 = new int[5] { 1, 2, 3, 4, 5, };
int[] arr4 = new int[] { 1, 2, 3, 4, 5, 6, 7 };
int[] arr5 = { 1, 2, 3, 4, 5 };
|
数组操作
1 2 3 4 5 6 7 8 9 10 11 12
|
int[] array = { 1, 2, 3, 4, 5 }; int arrayLength = array.Length; Console.WriteLine(arrayLength);
array[0] = 90; Console.WriteLine(array[0]);
|
遍历数组
通过循环快速获取数组中的每一个元素
1 2 3 4 5 6 7
|
int[] array = { 1, 2, 3, 4, 5 }; for (int i = 0; i < array.Length; i++) { Console.WriteLine(array[i]); }
|
增加和减少数组中的元素
数组初始化之后是不能直接添加新元素的
只能声明一个新数组来重新存储
增加就是大的装小的 减少就是少的装多的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| int[] array = { 1, 2, 3, 4, 5 }; int[] array1 = new int[10]; for (int i = 0; i < array.Length; i++) { array1[i] = array[i]; } array = array1;
int[] array = {1,2,3,4,5,6,7,8,9,10}; int[] array2 = new int[5]; for (int i = 0; i < array2.Length; i++) { array2[i] = array[i]; } array = array2;
|
查找数组的元素
使用循环遍历查找
1 2 3 4 5 6 7 8 9
| int[] array = { 1, 2, 3, 4, 5 }; for (int i = 0; i < array.Length; i++) { if (array[i]==3) { Console.WriteLine("查找到{0} 数组{1}号索引",array[i],i); } }
|
二维数组
数组的声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int[,] arr;
int[,] arr2 = new int[3, 3];
int[,] arr3 = new int[3, 3] {{1,2,3 }, {4,5,6 }, {7,8,9 }};
int[,] arr4 = new int[,] {{1,2,3 }, {4,5,6 }, {7,8,9 }};
int[,] arr5 = { {1,2,3 }, {4,5,6 }, {7,8,9 } };
|
基本操作
1 2 3 4 5 6 7 8 9 10
|
int arr2Row = arr2.GetLength(0); int arr2Columm = arr2.GetLength(1); Console.WriteLine("行" + arr2Row + "列"+arr2Columm);
int arrDeta = arr2 [1,1];
arr2 [1,1] = 90;
|
遍历数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };
int i, j;
for (i = 0; i < 5; i++) { for (j = 0; j < 2; j++) { Console.WriteLine(a[i,j]); }
}
|
增加和减少数组中的元素
使用两个 for 循环嵌套 来代表行列
大的装小的 :for 条件是小的数组长度
小的装大的:for 条件是小的数组长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int[,] arr5 = new int [3,3] { {1,2,3 }, {4,5,6 }, {7,8,9 } };
int[,] arr5add = new int[4, 3];
for (int i = 0; i < arr5.GetLength(0); i++) { for (int j = 0; j < arr5.GetLength(1); j++) { arr5add[i, j] = arr5[i, j]; } } arr5 = arr5add;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int[,] arr5 = new int [3,3] { {1,2,3 }, {4,5,6 }, {7,8,9 } };
int[,] arr5sub = new int[2, 3];
for (int i = 0; i < arr5sub.GetLength(0); i++) { for (int j = 0; j < arr5sub.GetLength(1); j++) { arr5sub[i, j] = arr5[i, j]; } } arr5 = arr5sub;
|
查找数组中的元素
遍历数组的基础上 增加一个 if 语句判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int[,] arr5 = new int [3,3] { {1,2,3 }, {4,5,6 }, {7,8,9 } };
for (int i = 0; i < arr5.GetLength(0); i++) { for (int j = 0; j < arr5.GetLength(1); j++) { if (arr5[i, j]==2) { Console.WriteLine("2为数组中的{0}行{1}列",i,j); }
} }
|
交错数组
行列可以不一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
int[][] arr;
int[][] arr2 = new int[3][];
int[][] arr3 = new int[3][] { new int[] { 1,2,3}, new int[] { 4,5}, new int[] { 1} };
int i = arr3.GetLength(0);
int j = arr3[0].Length;
int arr3Deta = arr3[1][1]; arr3[1][1] = 99;
for (int ii = 0; ii < arr3.GetLength(0); ii++) { for (int jj = 0; jj < arr3[ii].Length; jj++) { Console.Write(arr3[ii][jj]+" "); } Console.WriteLine(); }
int[][] arr3Add = new int[4][] { new int []{ 0,0,0}, new int []{ 0,0,0}, new int []{ 0,0,0}, new int []{ 0,0,0} }; for (int iii = 0; iii < arr3.GetLength(0); iii++) { for (int jjj = 0; jjj < arr3[iii].Length; jjj++) { arr3Add[iii][jjj] = arr3[iii][jjj]; } }
for (int ii = 0; ii < arr3Add.GetLength(0); ii++) { for (int jj = 0; jj < arr3Add[ii].Length; jj++) { Console.Write(arr3Add[ii][jj]+" "); } Console.WriteLine(); }
|
值和引用类型
引用类型: 数组,字符串,类
值类型: 有符号,无符号,浮点,字符,布尔,结构体
赋值规律
值类型在相互赋值时 是把内容拷贝给了对方 他变我不变
引用类型在相互赋值时 是把指针指向同一内存地址 他变我也变
值类型和引用类型存储的区域不同
值类型储存在 栈空间 —— 系统分配,自动回收,小而快
引用类型储存在 堆空间 ——手动申请和释放,大而慢
new 实例化
通过关键字 new 可以开一个新内存地址(在堆中新开一个房间)
这时候由于他们使用的不是同一个内存地址,自然赋值就不会牵扯到之前的数据
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int a = 10;
int[] array = new int {1,2,3,4,5};
int b = a;
int[] array2 = array;
b= 20; array2={5,4,3,2,1};
array2=new int[] {6,7,8,9};
|
特殊的引用类型 string
string 在 c#中进行了处理使其具有值类型的特征
虽然方便,但是频繁的改变 string 重新赋值会产生内存垃圾
1 2 3 4 5 6 7
| string str1 = "123"; string str2 = str1; str2 = "321";
|
函数
基本概念
函数(方法)是封装代码进行重复使用的一种机制,能够提升代码的复用率和抽象行为。函数在 class 和 struct 中声明,关键字有 static(静态的)和 void(无返回值)。
1 2 3 4 5 6 7 8 9 10 11
|
static int Function (int a ,int b) { int c = a*b; return c; }
|
基本语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| static void SayHello() { Console.WriteLine("你好世界"); }
static void SayYourName (string name) { Console.WriteLine("你的名字是{0}",name); }
static string WhatYouName () { return "Anii"; }
static int Multiply (int a ,int b) { int c = a*b; return c; }
static int[] Calc (int a ,int b) { int sum = a+b; int avg = (a+b)/2; int[] array = {sum,avg}; return array; }
static void Speak (string str) { if(str == "Damned") { return ""; } Console.WriteLine(str); }
|
ref 和 out 的作用
在 C#中,ref
和out
关键字用于参数传递,有以下作用:
ref 关键字:
- 用于传递参数的引用。当你使用
ref
关键字来声明方法参数时,传递给方法的是变量的引用而不是值的副本。这意味着方法可以修改调用者提供的变量的值。
out 关键字:
- 与
ref
类似,但有一些区别。out
关键字表示参数按引用传递,并且在方法内必须为其赋值。通常用于输出参数,方法可以在不返回值的情况下返回多个值。
具体来说:
- 使用
ref
关键字时,方法可以使用传递的参数引用来读取和修改参数的值。
- 使用
out
关键字时,方法必须在方法体内为参数赋值,而且调用方法时不需要提前为参数赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void ModifyValue(ref int x) { x = x * 2; }
int number = 10; ModifyValue(ref number); Console.WriteLine(number);
void GetValues(out int a, out int b) { a = 10; b = 20; }
int resultA, resultB; GetValues(out resultA, out resultB); Console.WriteLine(resultA); Console.WriteLine(resultB);
|
总结:
ref
和out
关键字允许方法修改调用者提供的变量。
out
关键字要求在方法内为参数赋值,而ref
关键字不要求。
params 变长参数
可以输入不定的多个参数,并把这些参数存入数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static int Sum(params int[] arr) { int sum = 0; for (int i = 0; i < arr.Length; i++) { sum += arr[i]; } return sum; }
int sum = Sum(1,2,3,4,5,6);
|
在函数参数中只能最多出现一个 params 关键字 且一定在最后一组参数
1 2 3 4
| static int Sum(int a ,int b ,params int[] arr) { return ; }
|
函数重载
在同一语句块(class 或 struct)中函数名相同 参数的数量 or 类型 or 顺序不同。
系统根据 参数:数量,类型,顺序 的不同来重载不同的函数。
与返回值类型无关 返回值类型可以是任意类型
作用:命名一组功能类似的函数,减少函数名的数量,避免命名空间的污染,提升程序的可读性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| static int CalcSum(int a ,int b) { return a+b; }
static int CalcSum(int a ,int b,int c) { return a+b+c; }
static float CalcSum(float a ,float b) { return a+b; }
static float CalcSum(int a , float b) { return a+b; } static float CalcSum(float b ,int a ) { return b+a; }
static float CalcSum(ref float b ,int a ) { return b+a; }
CalcSum(1,2); CalcSum(1,2,3); CalcSum(1.11f,2.22f); CalcSum(1,2.22f); CalcSum(2.22f,1);
|
递归函数
让函数自己调用自己
必须有结束调用的条件
比较难且实际开发用的不多,面试客户端会有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| static void Function(int a) { if (a>10) { return; } Console.WriteLine(a); ++a; Function(a); }
int a =0; Function(a);
|
结构体
基础概念
在 namespact(命名空间)中声明结构体
命名规范:首字母大写
变量和函数的集合 用来表示特定的数据集合
封装逻辑和行为
可以实例化很多个 实现复用
访问修饰符
1
| **public 公开的 可以被外部访问** <br />**private 私有的 只能内部使用** <br />**没有添加时默认是private 的**
|
构造函数
没有返回值,函数名和结构名相同,主要用于快速初始化结构体对象的
结构体中构造函数和函数也是可以重载的
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| using System;
namespace Lesson_4_结构体 { struct Student {
public int age; public bool sex; public int number; public string name;
public Student(int age, bool sex, int number, string name) { this.age = age; this.sex = sex; this.number = number; this.name = name;
}
public void Speak() { Console.WriteLine("我的名字是{0},我今年{1}岁", name, age); } }
class Program { static void Main(string[] args) { Student s1; s1.name = "苏先生"; s1.age = 20; s1.number = 123321123; s1.sex = true; s1.Speak();
Student s2; s2.name = "苏小姐"; s2.age = 20; s2.number = 123371524; s2.sex = false; s2.Speak();
Student s3 = new Student(18, true, 2, "小红"); s3.Speak(); } } }
|
排序
给定一个数组,进行有规律排序(比较大小)
冒泡排序
两两相邻
不停比较
不停交换
比较 m 轮
两层循环
外层轮数
内层比较
两值比较
满足交换
优化:比过的不比,加入 bool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int[] array = { 9, 5, 7, 6, 3, 8, 2, 1, 4 };
bool isSort = false;
for (int m = 0; m < array.Length; m++) { isSort = false; for (int n = 0; n < array.Length - 1-m; n++) { if (array[n] > array[n + 1]) { isSort = true; int temp = array[n]; array[n] = array[n + 1]; array[n + 1] = temp; } } if (!isSort) { break; } }
for (int i = 0; i < array.Length; i++) { Console.WriteLine(array[i]); }
|
选择排序
新建中间商
依次比较
找出极值
放入目标位置
比较 n 轮
两层循环
外层轮数
内层寻找
初始索引
记录极值
内存循环外交换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| int[] array = { 9, 5, 7, 6, 3, 8, 2, 1, 4 }; for (int m = 0; m < array.Length; m++) { int index = 0; for (int n = 0; n < array.Length-m; n++) { if (array[index] < array[n]) { index = n; } } if (index != array.Length-1-m) { int temp = array[index]; array[index] = array[array.Length - 1 - m]; array[array.Length - 1 - m] = temp; } }
for (int i = 0; i < array.Length; i++) { Console.WriteLine(array[i]); }
|