一、局部方法(Main内部定义方法)
1. 核心概念
可以在 Main 方法内部直接定义普通方法,叫做局部方法。
作用:专门给当前 Main 内部逻辑使用,不用写在类的外部。
2. 就近原则(超级重点)
当内部方法和外部方法重名时,优先调用离调用位置最近的【内部局部方法】
3. 基础语法案例
// 无返回值局部方法
void F1()
{
Console.WriteLine("我是在main里面定义的方法");
}
F1();
// 有返回值局部方法
int F2(int a,int b)
{
return a + b;
}
Console.WriteLine(F2(10, 20));
4. 数组高阶方法传统写法(2.0版本)
在Main内部定义回调函数,传递给数组高阶方法
int[] ages = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine(Array.Find(ages, F3)); // 2
// 局部回调方法
bool F3(int v)
{
return v % 2 == 0;
}
缺点:代码冗余、繁琐、不够简洁,因此引出 Lambda 表达式。
二、Lambda 表达式(必考核心)
1. 本质定义(必背)
Lambda 本质是匿名函数(箭头函数) ,专门用来当做**方法参数(回调函数)**使用。
2. 完整语法
(参数列表) => { 函数体 }
3. 五种精简规则(默写满分)
-
- 参数类型可以自动推断,直接省略
-
- 只有一个参数:可以省略小括号
-
- 函数体只有一行代码:可以省略大括号
-
- 单行代码是返回值:可以省略 return 关键字
-
- 无参数、多参数:小括号不能省略
4. 完整写法 → 极简写法演变
// 完整版
Func<int, int, int> F4 = (int a, int b) => { return a + b; };
// 精简1:省略参数类型
Func<int, int, int> F5 = (a, b) => a + b;
// 精简2:单个参数省略括号
Func<int, int> F6 = a => a;
三、三大委托类型(接收Lambda函数|必考)
Lambda 不能单独存在,二选一:要么直接当参数传递,要么用委托变量接收
1. 三大委托终极区分(死记)
-
Action<> :接收 无返回值 方法(只干活,不返回)
-
Func<> :接收 有返回值 方法,最后一个泛型是返回值类型
-
Predicate<> :接收 返回值固定为bool 的方法(专门给数组高阶方法用)
2. 易错大坑(考试必考)
Func<int,bool> 和 Predicate<int> 不能互相转换
数组 Find、FindAll 只支持 Predicate,不支持 Func!
// 正确写法
Predicate<int> F8 = v => v % 2 != 0;
Array.Find(ages, F8);
// 错误写法!不能混用
Func<int, bool> F7 = v => v % 2 != 0;
Array.Find(ages, F7); // 报错:无法从Func转为Predicate
四、数组高阶方法四种写法版本(从繁到简)
1.0 最原始:方法定义在类外部(最繁琐)
2.0 进阶:方法定义在Main内部(局部方法)
3.0 高级:Lambda用变量接收
Predicate<int> F8 = v => v % 2 != 0;
Array.Find(ages, F8);
4.0 终极最简:直接内嵌Lambda(工作常用)
// 直接当做参数传入,最简洁
Console.WriteLine(Array.Find(ages, v => v % 2 != 0));
五、Lambda + 数组高阶实战案例(全覆盖)
string[] strings = { "吴亦凡", "罗志祥", "郑爽", "东北雨姐" };
// 1. 查找第一个长度为2的字符串
Console.WriteLine(Array.Find(strings, v => v.Length == 2));
// 2. 查找第一个长度为2的字符串索引
Console.WriteLine(Array.FindIndex(strings,v=>v.Length==2));
// 3. 查找以"吴"开头
Console.WriteLine(Array.Find(strings, v => v.StartsWith("吴")));
// 4. 查找以"祥"结尾
Console.WriteLine(Array.Find(strings, v => v.EndsWith("祥")));
// 5. 多条件:结尾是姐 并且长度4
Console.WriteLine(Array.Find(strings, v => v.EndsWith("姐") && v.Length==4 ));
六、Where 筛选扩展方法(进阶考点)
1. 作用
根据条件筛选数组元素,返回可迭代集合,需要 .ToArray() 转回数组
// 筛选长度为3的所有字符串,转为新数组
string[] ss = strings.Where(a => a.Length == 3).ToArray();
Console.WriteLine(string.Join("-", ss));
七、全套易错点避坑总结
-
- 局部方法就近原则:内部方法优先于外部同名方法
-
- Lambda是匿名函数,不能单独语句存在,必须赋值或当参数
-
- 单参数可省括号,无参/多参数不能省括号
-
- Func有返回值、Action无返回值、Predicate固定返回bool
-
- 数组高阶方法只识别 Predicate,不识别 Func(经典报错)
-
- Where筛选结果不是数组,必须 ToArray() 转换
八、终极默写短句(考前速记)
-
局部方法写在Main内部,就近调用优先级最高
-
Lambda匿名箭头函数,专做回调参数超简洁
-
单参省括号,单行省大括号,单行返回省return
-
Action无返回,Func有返回,Predicate布尔专用
-
数组高阶找条件,Predicate专属不报错
-
Where筛选转数组,必须调用ToArray
------------三大委托(Func / Action / Predicate)区别---------
一、三大委托核心总纲(必背区分)
委托:用来接收Lambda匿名函数 / 方法的类型,是数组高阶方法的底层支撑。
三大委托唯一区别:有无返回值、返回值类型、参数规则
-
Func<>:有返回值函数,最后一个泛型为返回值类型,前面全是参数类型
-
Action<>:无返回值函数,只有参数类型,无返回值
-
Predicate<> :专属布尔委托,只能传1个参数,固定返回bool
二、Func<> 委托(有返回值)
1. 核心规则
语法:Func<参数类型1,参数类型2...,返回值类型>
特点:必须有返回值,支持多个参数,最后一个泛型永远是返回值类型
2. 实战案例:判断素数
需求:接收一个int数字,判断是否为素数,返回bool值
// Func<int,bool> 参数int,返回值bool
Func<int, bool> F1 = a =>
{
bool isShuSu = true;
// 素数判断:只能被1和自身整除
for (int i = 2; i < a; i++)
{
if (a % i == 0)
{
isShuSu = false;
break;
}
}
return isShuSu;
};
Console.WriteLine(F1(8)); // false 8不是素数
// 搭配All高阶方法:判断数组所有元素是否全为素数
int[] ages = {3,5,7};
Console.WriteLine(ages.All(F1)); // true 全部是素数
3. 考点总结
-
可以单独调用委托方法
-
可搭配 All()、Any() 等高阶扩展方法使用
-
泛型顺序:参数在前,返回值在后
三、Action<> 委托(无返回值)
1. 核心规则
语法:Action<参数类型1,参数类型2...>
特点:无返回值,只执行逻辑、做操作,不需要return
2. 实战案例:筛选打印偶数
// 接收int参数,无返回值
Action<int> F2 = a =>
{
if (a % 2 == 0)
{
Console.WriteLine(a);
}
};
F2(3); // 单独调用,3是奇数无输出
// 搭配 Array.ForEach 高阶遍历方法
int[] ages1 = { 3, 5, 7,4 };
Array.ForEach(ages1, F2); // 遍历数组,仅打印偶数4
3. 考点总结
-
专门用于纯执行逻辑(打印、修改数据)
-
是 Array.ForEach() 专属匹配委托
-
绝对不能写return 返回数据
四、Predicate<> 布尔专属委托
1. 核心规则(必考)
语法:Predicate<参数类型>
-
有返回值,返回值固定为 bool
-
只能接收一个参数(硬性规则)
-
数组所有条件查询方法专属委托(Find、FindAll、FindIndex)
2. 实战案例:判断平方是否大于25
// 单个int参数,固定返回bool
Predicate<int> F3 = (a) =>
{
if (a * a > 25)
{
return true;
}
return false;
};
Console.WriteLine(F3(6)); // 6*6=36>25 → true
// 搭配FindAll:筛选所有平方大于25的元素
int[] ages2 = { 3, 5, 7, 4,8 };
int[] arr = Array.FindAll(ages2, F3);
Console.WriteLine(string.Join("-",arr)); // 7-8
五、三大委托终极对比表(简答题满分)
| 委托类型 | 返回值 | 参数规则 | 常用场景 |
|---|---|---|---|
| Func<> | 有返回值(任意类型) | 支持多个参数 | 数据计算、条件判断、All/Any |
| Action<> | 无返回值 | 支持多个参数 | 遍历执行、打印、纯操作逻辑 |
| Predicate<> | 固定 bool | 只能一个参数 | 数组Find、FindAll条件查询 |
六、高频易错坑点
-
- Func最后一个泛型是返回值,前面全部是参数,顺序不能乱
-
- Action绝对没有返回值,写return数据直接报错
-
- Predicate仅限单个参数、返回bool,多参数不能用
-
- 数组Find/FindAll只能用Predicate,不能用Func(类型不匹配)
-
- ForEach遍历方法只能匹配Action无返回值委托
七、考前速记短句
-
Func有返回,参数在前值在后,计算判断都能用
-
Action无返回,只做执行不回传,遍历打印专属它
-
Predicate单参数,固定返回布尔值,数组查询专用
手写 Array.Find 底层源码原理(自定义MyArray.Find)
一、核心意义(必考压轴)
我们平时使用的 Array.Find() 是系统封装好的方法。 手写 MyArray.Find() 可以彻底看透:高阶方法 + 委托回调 + 底层遍历原理。
所有数组高阶方法本质:循环遍历 + 回调函数判断
二、自定义 Find 方法完整源码
public class MyArray
{
// 自定义Find底层原理
// 参数1:目标数组
// 参数2:Func委托(条件回调函数)
public static int Find(int[] arr,Func<int,bool> match)
{
// 1. 判空校验(系统源码标准写法)
if (arr == null)
{
throw new ArgumentNullException("array");
}
if (match == null)
{
throw new ArgumentNullException("match");
}
// 2. 遍历数组所有元素
for (int i = 0; i < arr.Length; i++)
{
// 3. 执行用户传入的Lambda条件
if (match(arr[i]))
{
// 满足条件,立即返回当前元素
return arr[i];
}
}
// 4. 全部遍历完毕,没有满足条件返回默认值
return 0;
}
}
三、参数详解(必背)
1. 第一个参数 int\[\] arr
需要被查询的目标数组(例如:ages数组)
2. 第二个参数 Func<int,bool> match
就是我们传入的 Lambda 条件函数
-
参数 int:代表数组每一个元素
-
返回值 bool:代表是否满足条件
-
match 本质就是回调函数,由我们传、系统源码调用
四、代码执行流程(超级重点|默写流程)
测试代码
int[] ages = new int[] { 1, 2, 3 };
// 找第一个偶数
Console.WriteLine(MyArray.Find(ages, v => v % 2 == 0));
// 找第一个能被3整除的数
Console.WriteLine(MyArray.Find(ages, v => v % 3 == 0));
执行步骤(逐行拆解)
-
把 ages数组 、Lambda条件函数 传入 Find 方法
-
先判断数组、委托是否为null,为空抛出参数为空异常
-
进入for循环,逐个取出数组元素 arri
-
**自动执行 match(元素)**也就是执行 Lambda 条件
-
条件返回true → 立刻返回当前元素,终止方法
-
全部不满足 → 返回类型默认值 0
五、两个核心面试真相
1. 为什么Find只返回【第一个】满足条件的元素?
因为 一旦匹配成功直接return,方法结束,不再继续循环。
2. 找不到元素为什么返回 0?
int类型默认值就是0,系统Array.Find 也是同样逻辑: 值类型返回默认值,引用类型返回null。
六、C# 三大必考异常(你代码中标注)
1. 索引越界异常
IndexOutOfRangeException
原因:访问下标 <0 或 >= 数组长度
int[] ages = {1,2,3};
Console.WriteLine(ages[10]); // 越界报错
2. 除零异常
DivideByZeroException
原因:整数除以 0
int a = 10;
Console.WriteLine(a/0);
3. 参数为空异常(源码核心校验)
ArgumentNullException
触发时机:数组为null 或 委托传null
七、必背核心结论(简答题满分)
-
高阶方法本质:底层for循环 + 委托回调判断
-
Lambda是实参,传递给方法的委托参数
-
委托 match 是由自己定义逻辑,源码主动调用
-
Find 匹配到第一个true立刻返回,终止循环
-
无匹配元素返回当前类型默认值
-
源码优先判空,防止空引用报错
八、终极默写短句
-
Find方法底层for遍历,委托函数做判断
-
条件为真立即返回第一个,全部不真返回默认值
-
数组委托先判空,为空抛出参数空异常
-
下标越界查索引,整数除零报除零异常