本篇核心知识点:foreach 遍历、循环跳转关键字 (break/continue/return)、面向对象封装与访问修饰符、函数完整规范、函数参数传递(值传递 /ref/out)、函数重载、Lambda 匿名函数、闭包陷阱、可空类型与空合并运算符、一维 / 多维数组、Random 随机工具、洗牌算法
一、foreach 循环遍历
1. 概念
专门用于遍历数组、集合(List 等)的简化循环,直接取出容器内元素本身 ,不提供索引,等价于 TS/JS 的for of,区别于 TSfor in(取索引)。
特性
-
语法格式:
foreach(元素类型 变量 in 容器) -
仅只读遍历,遍历过程中不能修改容器长度(增删元素会报错);
-
无需手动定义循环变量、控制边界,代码简洁。
代码示例
int[] arr = { 10, 20, 30, 40 };
// foreach遍历数组元素
foreach(int n in arr)
{
Console.Write(n + " ");
}
拓展
需要获取下标时,改用普通for循环;集合批量只读遍历优先使用 foreach。
二、循环跳转关键字
1. continue
概念:
终止当前单次循环,直接进入下一轮循环判断。
特性:
仅跳出本轮剩余代码,外层循环不受影响。
2. break
概念:
直接跳出当前所在一层循环,外层循环继续执行。
特性:
多层嵌套循环仅退出最内层;如需一次性退出所有循环可用 return。
3. return
概念:
直接终止整个方法,所有循环同步结束,适合找到目标后直接返回结果。
代码示例(区分三者)
for(int i = 1; i <= 10; i++)
{
if(i == 3) continue; // 跳过3,继续下一轮
if(i == 6) break; // 循环直接终止
Console.WriteLine(i);
}
三、面向对象封装 & 访问修饰符
3.1 封装概念
将类的数据与功能封装在类内部,隐藏内部实现细节,仅对外暴露安全访问入口,避免外部随意篡改成员。
3.2 四大访问修饰符(权限从大到小)
-
public 公共
概念:任何类、任何程序集均可访问;
场景:对外暴露方法、属性。
-
internal 程序集内
概念:仅当前项目(程序集)内所有类可访问,跨项目无法调用;
C++/Java 无该修饰符,C# 独有。
-
protected 受保护
概念:本类 + 派生子类可访问,外部普通类无权访问;
场景:父类预留给子类复用的成员。
-
private 私有(类默认权限)
概念:仅当前类内部可访问,外部、子类都无法读写;
规范:所有字段默认 private,通过 public 方法 / 属性操作。
特性
C# 类、成员默认访问权限为 private;TS 类默认 public,二者核心区别。
代码示例
class Player
{
private int hp; // 私有,外部无法直接修改
protected float speed;// 子类可访问
public void Move() // 公开对外方法
{
hp -= 10;
}
}
四、函数(方法)完整规范
1. 函数组成四要素
返回类型 + 函数名 + 参数列表 + 函数体
-
返回类型:C# 不可省略,无返回写
void(TS 可省略); -
命名规范:大驼峰 Pascal 命名(首字母大写);
-
函数不能嵌套定义(方法内部不能再写独立函数,仅允许 Lambda);
-
文档注释
///描述功能、参数、返回值,团队协作必备。
代码示例
/// <summary>角色横向移动</summary>
/// <param name="speed">移动速度</param>
public void MoveX(float speed)
{
x += speed;
}
2. 递归函数
概念:
函数内部调用自身,分治简化代码,必须设置递归终止条件,否则死循环栈溢出。
经典案例:
阶乘、斐波那契数列
// 求n阶乘 n! = n*(n-1)!
int Factorial(int n)
{
if(n == 1) return 1; // 终止条件
return n * Factorial(n - 1);
}
拓展:
递归优势代码简洁;缺点深度过大会栈溢出,大数据优先迭代循环。
五、函数三种参数传递(面试高频 ref/out/ 值传递)
5.1 值传递(默认方式)
概念:
实参拷贝一份副本传入函数,函数内修改副本不影响外部原始变量。
特性:
值类型默认值传递;引用类型本质传递地址(特殊)。
void Change(int a)
{
a++;
}
int hp = 100;
Change(hp);
// hp依旧等于100,无变化
5.2 ref 引用传递
概念:
传递变量别名,函数与外部共享同一块内存,内部修改同步作用于外部。
硬性规则:
-
变量传入前必须提前初始化;
-
函数内可修改、可不修改该参数。
void ChangeRef(ref int a)
{
a++;
}
int hp = 100; // 必须初始化
ChangeRef(ref hp);
// hp = 101
5.3 out 输出参数(C# 独有)
概念:
专门用于一个函数返回多个结果,弥补 return 仅能返回一个值的限制。
硬性规则(与 ref 核心区别)
-
变量传入无需提前初始化;
-
函数内部必须对 out 参数赋值,编译强制校验,不赋值报错;
游戏实战:
射线检测(return 返回是否碰撞,out 输出碰撞点信息)
// 一个返回值+out输出第二个结果
bool Calc(int x, int y, out int sumY)
{
sum = x + y; // out必须赋值
return x > y;
}
// out变量无需初始化
int resY;
bool flag = Calc(2, 4, out resY);
ref vs out 对比表
| 对比项 | ref | out |
|---|---|---|
| 传入前 | 必须初始化 | 无需初始化 |
| 函数内 | 可改可不改 | 必须赋值 |
| 用途 | 双向读写外部变量 | 仅向外输出计算结果 |
拓展:
数组 / 自定义类为引用类型,直接传递地址,无需 ref 即可修改内部元素
void ModifyArr(int[] data)
{
data[0] = 999;
}
int[] arr = {1,2,3};
ModifyArr(arr);
// 数组第一个元素被修改
六、函数重载
概念:
同一作用域(同类内),函数名相同、参数列表不同 ,编译器根据实参自动匹配对应函数,属于编译期静态多态。
合法重载条件(同时满足)
-
函数名完全一致;
-
参数个数 / 参数类型 / 参数顺序至少一处不同;
不参与重载判断:返回值、访问修饰符
// 合法重载
public int Add(int a, int b) { return a + b; }
public float Add(float a, float b) { return a + b; }
// 仅返回值不同,非法重载,编译报错
// public void Add(int a, int b){}
拓展:
泛型可大量减少重复重载代码。
七、Lambda 匿名函数(委托基础)
1. 概念
无独立函数名的内联函数,等价 TS 箭头函数,底层依托Action/Func委托类型,常用于事件、集合筛选、回调。
分类
-
Action:无返回值委托; -
Func<T>:带返回值委托。
基础语法
// 无参数无返回
Action act = () => Console.WriteLine("Hello");
act();
// 带两个参数,有返回值 Func<入参1,入参2,返回值>
Func<int,int,int> Add = (a,b) => a + b;
Console.WriteLine(Add(3,5));
2. Lambda 捕获外部变量 & 闭包陷阱
概念:
Lambda 可捕获方法内局部变量,形成闭包。
经典 bug:
循环中直接捕获循环变量 i,所有 Lambda 共享同一个 i(循环结束 i 为固定值)
解决方案:
循环内创建临时变量拷贝 i,每个 Lambda 捕获独立副本
List<Action> list = new List<Action>();
for(int i = 0; i < 3; i++)
{
// 错误:捕获共享i
list.Add(()=>Console.WriteLine(i));
// 正确:临时变量拷贝,独立值
int temp = i;
list.Add(()=>Console.WriteLine(temp));
}
实战场景:
按钮点击事件、集合 Where 筛选、异步回调
八、可空类型 & 空合并运算符
8.1 可空类型 值类型?
概念:
C# 值类型(int/bool/double)默认不能为 null,加问号?变为可空类型,支持赋值 null;引用类型天然可空。
代码示例
int? num = null; // 合法
int a = num ?? 0;// 空合并给默认值
8.2 空合并运算符 ??
概念:
判断左侧可空变量,若为 null,返回右侧默认值;不为 null 返回自身。
特性:
仅处理 null 空值,解决可空类型赋值报错问题。
double? score = null;
double real = score ?? 60.0; // score为空,赋值60
拓展:
数据库字段大量使用可空类型(字段无数据存 null)。
九、数组(一维、多维)
9.1 一维数组
概念:
连续内存存储同类型数据,长度固定不可扩容,下标从 0 开始,越界抛异常。
两种初始化方式
-
动态初始化(指定长度,默认 0)
int[] arr = new int[6];
-
静态初始化(直接赋值元素)
int[] arr = {1,2,3,4,5};
属性:数组.Length 获取元素个数(属性,非方法,无括号)
9.2 多维数组(C# 特殊语法)
概念:
不用双层[][],使用逗号分隔维度 int[,];三维int[,,]
二维数组示例(3 行 4 列)
// 静态初始化
int[,] map = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
// 访问第二行第三列
Console.WriteLine(map[1,2]);
特性
-
内存整块连续;
-
长度固定,无法动态增减;
-
TS/Java 使用
[][]交错数组,C# 多维数组语法区分。
十、Random 随机类 & 洗牌算法
1. Random 工具类
概念:
生成伪随机数,全局仅实例化一次,循环内重复 new 会生成相同序列。
核心方法
-
Next():0~int 最大值随机整数; -
Next(max):0 ≤ x < max; -
Next(min,max):min ≤ x < max; -
NextDouble():0~1 浮点随机数。// 全局只创建一次
static Random rd = new Random();
int num = rd.Next(1, 101); // 1~100
2. 洗牌算法(数组乱序)
思路:
从后往前,当前下标与随机下标交换,打乱数组元素。
static void Shuffle(int[] arr)
{
Random rd = new Random();
for(int i = arr.Length - 1; i > 0; i--)
{
int idx = rd.Next(0, i + 1);
int temp = arr[i];
arr[i] = arr[idx];
arr[idx] = temp;
}
}
拓展:
游戏道具随机掉落、卡牌洗牌核心算法。
十一、面试 & 工程拓展考点
-
ref/out 核心区别、射线检测 out 参数实战;
-
Lambda 闭包循环捕获陷阱,项目高频 bug;
-
四种访问修饰符权限区分,internal 独有特性;
-
函数重载判断标准,返回值不参与重载;
-
可空类型
?与??空合并运算符数据库用途; -
Random 禁止循环重复实例,伪随机数原理;
-
C# 多维数组与 TS/Java 交错数组语法差异。