C# 局部方法 + Lambda表达式 + 三大委托和三大委托的区别和手写 Array.Find 底层源码原理(自定义MyArray.Find)

一、局部方法(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. 五种精简规则(默写满分)

    1. 参数类型可以自动推断,直接省略
    1. 只有一个参数:可以省略小括号
    1. 函数体只有一行代码:可以省略大括号
    1. 单行代码是返回值:可以省略 return 关键字
    1. 无参数、多参数:小括号不能省略

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));

七、全套易错点避坑总结

    1. 局部方法就近原则:内部方法优先于外部同名方法
    1. Lambda是匿名函数,不能单独语句存在,必须赋值或当参数
    1. 单参数可省括号,无参/多参数不能省括号
    1. Func有返回值、Action无返回值、Predicate固定返回bool
    1. 数组高阶方法只识别 Predicate,不识别 Func(经典报错)
    1. 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条件查询

六、高频易错坑点

    1. Func最后一个泛型是返回值,前面全部是参数,顺序不能乱
    1. Action绝对没有返回值,写return数据直接报错
    1. Predicate仅限单个参数、返回bool,多参数不能用
    1. 数组Find/FindAll只能用Predicate,不能用Func(类型不匹配)
    1. 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));

执行步骤(逐行拆解)

  1. ages数组Lambda条件函数 传入 Find 方法

  2. 先判断数组、委托是否为null,为空抛出参数为空异常

  3. 进入for循环,逐个取出数组元素 arri

  4. **自动执行 match(元素)**也就是执行 Lambda 条件

  5. 条件返回true → 立刻返回当前元素,终止方法

  6. 全部不满足 → 返回类型默认值 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遍历,委托函数做判断

  • 条件为真立即返回第一个,全部不真返回默认值

  • 数组委托先判空,为空抛出参数空异常

  • 下标越界查索引,整数除零报除零异常

相关推荐
weixin_4280053015 小时前
C#调用 AI学习从0开始-第2阶段(Function Calling+工具调用智能体)-第8天Function Calling原理
人工智能·学习·c#·functioncalling
十年伴树15 小时前
python --version返回空行
开发语言·python
我是一颗柠檬15 小时前
【JDK8新特性】新工具类与API改进Day11
java·开发语言·后端·intellij-idea
牧瀬クリスだ15 小时前
多线程安全:从原子性到锁机制
java·开发语言
froginwe1115 小时前
Python File 方法
开发语言
Hanniel15 小时前
Python 元类(中):拦截类的创建
开发语言·python
我能坚持多久15 小时前
STL详解——priority_queue的使用以及模拟实现
开发语言·c++·priority_queue
Tony Bai15 小时前
从 Go 迁移到 Rust
开发语言·后端·golang·rust
雪豹阿伟15 小时前
12.C# —— 经典排序 +(ArrayList / 泛型 List / Dictionary)
c#·上位机