Lambda 表达式是 C# 函数式编程的核心,尤其在 Entity Framework (EF) 数据查询、集合操作中是必备技能。本文基于我的学习笔记全面优化,补充无返回值/有返回值完整实例、新手避坑指南,帮你彻底掌握 Lambda 用法。
一、Lambda 核心概念与简化规则
Lambda 是匿名函数的简写形式,核心语法:(参数) => 表达式/语句块,=> 读作“goes to”(输入参数→输出结果)。
先修正新手高频错误(类型匹配)
原始笔记中典型错误:委托类型与参数类型不匹配,编译器直接报错:
// ❌ 错误:Action<string> 要求参数是string,但匿名委托参数是int
Action<string> a1 = delegate(int i) { Console.WriteLine(i); };
// ✅ 正确:Action<int> 匹配 int 类型参数(核心:委托类型 ≡ 参数类型)
Action<int> a1 = delegate(int i) { Console.WriteLine(i); };
Lambda 简化过程(以“打印整数”为例)
以下每一步代码均可直接运行,逐步体会“从繁到简”的核心逻辑:
| 简化阶段 | 代码示例 | 核心简化点 |
|---|---|---|
| 1. 传统匿名委托 | Action<int> a1 = delegate(int i) { Console.WriteLine(i); }; | 无(最基础写法,无简化) |
| 2. 替换为 Lambda | Action<int> a2 = (int i) => { Console.WriteLine(i); }; | delegate 关键字 → => 符号 |
| 3. 省略参数类型 | Action<int> a3 = (i) => { Console.WriteLine(i); }; | 编译器根据委托类型自动推断参数类型 |
| 4. 省略参数括号(单参数) | Action<int> a4 = i => { Console.WriteLine(i); }; | 单个参数时,括号可省略(多参数必须保留) |
| 5. 省略大括号(单行语句) | Action<int> a5 = i => Console.WriteLine(i); | 语句体仅一行时,大括号可省略 |
无返回值 vs 有返回值 Lambda 核心规则
| 类型 | 委托基类 | 核心规则 | 示例 |
|---|---|---|---|
| 无返回值 | Action<T> | 单行可省 {},无 return | i => Console.WriteLine(i) |
| 有返回值 | Func<T,TResult> | 单行可省 {} + return,多行必须手动 return | (i,j) => i+j(单行)(i,j) => { var s=i+j; return s; }(多行) |
带返回值的 Lambda 简化(关键规则)
如果委托有返回值(如 Func),且方法体只有一行代码且是返回值,可省略 {} 和 return:
// 传统匿名委托(带返回值)
Func<int, int, string> f1 = delegate (int i, int j)
{
return "结果是" + (i + j);
};
// Lambda 极简写法(省略 {} 和 return)
Func<int, int, string> f2 = (i, j) => "结果是" + (i + j);
// 调用f1(匿名委托)
Console.WriteLine(f1(4,5)); // 输出:f1结果是:8
// 调用f2(Lambda表达式)
Console.WriteLine(f2(3, 5)); // 输出:f2结果是:8
二、核心实例:无返回值/有返回值 Lambda 完整演示
1. 无返回值 Lambda(Action 委托)
核心用于“执行操作但不返回结果”(打印、修改数据、遍历),覆盖单参数/多参数/无参数场景:
namespace _03_无返回值_Lambda_Action_委托_
{
internal class Program
{
static void Main(string[] args)
{
// 1. 单参数无返回值(最简写法)
Action<int> printNum = num => Console.WriteLine($"数字:{num}");
printNum(100);
// 2. 多参数无返回值(拼接信息)
Action<string, int> printUser = (name, age) => Console.WriteLine($"姓名:{name},年龄:{age}");
printUser("张三", 25);
// 3. 无参数无返回值(固定逻辑)
Action printHello = () => Console.WriteLine("Hello Lambda!");
printHello();
Console.ReadLine();
}
}
}
2. 有返回值 Lambda(Func 委托)
核心用于“执行逻辑并返回结果”(计算、筛选、转换),覆盖单行/多行场景:
namespace _04_有返回值_Lambda_Func_委托_
{
internal class Program
{
static void Main(string[] args)
{
// 1. 单行简化(自动返回,省略 return)
Func<int, int> getSquare = num => num * num;
Console.WriteLine($"5的平方:{getSquare(5)}");
// 2. 多行语句(必须手动 return)
Func<int, string> checkAdult = age =>
{
Console.WriteLine($"检查年龄:{age}"); // 中间操作
return age >= 18 ? "成年" : "未成年";
};
Console.WriteLine(checkAdult(20));
Console.ReadLine();
}
}
}
三、实战练习(带完整代码+运行结果+新手解析)
练习一:Lambda 基础简化(验证简化规则)
目标:逐行验证 Lambda 从匿名委托到极简写法的过程
完整代码:
using System;
class Program
{
static void Main(string[] args)
{
// 1. 传统匿名委托
Action<int> a1 = delegate(int i) { Console.WriteLine($"版本1:{i}"); };
a1(10);
// 2. 基础 Lambda(替换 delegate 为 =>)
Action<int> a2 = (int i) => { Console.WriteLine($"版本2:{i}"); };
a2(20);
// 3. 省略参数类型(编译器自动推断)
Action<int> a3 = (i) => { Console.WriteLine($"版本3:{i}"); };
a3(30);
// 4. 省略括号+大括号(单参数+单行极简)
Action<int> a4 = i => Console.WriteLine($"版本4:{i}");
a4(40);
Console.ReadLine();
}
}
运行结果:
版本1:10
版本2:20
版本3:30
版本4:40
新手解析:每一步简化都不改变逻辑,仅减少冗余语法,编译器会自动补全推断的信息。
练习二:Select 处理整数集合(生成新集合)
目标:用 Select 转换集合元素(核心:“一对一”处理)
完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
List<int> list1 = new List<int> { 1, 2, 3, 8, 16, 99 };
// Select:对每个元素拼接"你好",生成新的字符串集合
IEnumerable<string> data = list1.Select(i => i + "你好");
// 遍历输出新集合
foreach (var item in data)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
运行结果:
1你好
2你好
3你好
8你好
16你好
99你好
新手解析:Select 是“转换器”,原集合 list1 不变,新集合 data 是转换后的结果。
练习三:Where 过滤整数集合(筛选元素)
目标:用 Where 按条件筛选元素(核心:Lambda 返回 bool)
完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
List<int> list1 = new List<int> { 1, 2, 3, 8, 16, 99 };
// Where:筛选大于10的元素(Lambda 返回 true 则保留)
IEnumerable<int> data = list1.Where(i => i > 10);
Console.WriteLine("大于10的元素:");
foreach (var item in data)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
运行结果:
大于10的元素:
16,99
新手解析:Where 的 Lambda 必须返回 bool 类型,true 保留元素,false 过滤掉。
练习四:对象集合求和(Sum 方法)
目标:计算对象集合中指定属性的总和
完整代码:
internal class Program
{
static void Main(string[] args)
{
Person[] p = new Person[]
{
new Person { Name="chen", Age=12 },
new Person { Name="chen2", Age=24 },
new Person { Name="chen3", Age=12 }
};
// Sum:Lambda 指定“要累加的属性(Age)”
int totalAge = p.Sum(i => i.Age);
// 类型转换:数组 ↔ 列表(EF中常用)
List<Person> ps = p.ToList();
Person[] ps4 = p.ToArray();
Console.WriteLine("年龄总和:" + totalAge); // 12+24+12=48
Console.ReadKey();
}
class Person
{
public string Name { get; set; } // 姓名
public int Age { get; set; } // 年龄
}
}
运行结果:
年龄总和:48
新手解析:Sum 的 Lambda 需返回数值类型(int/double),指定“要求和的属性”即可自动累加。
练习五:对象集合综合操作(OrderBy/FirstOrDefault)
目标:组合多个扩展方法处理对象集合(EF 高频场景)
完整代码:
internal class Program
{
static void Main(string[] args)
{
Person[] p = new Person[]
{
new Person { Name="chen", Age=12 },
new Person { Name="chen2", Age=24 },
new Person { Name="chen3", Age=12 },
new Person { Name="chen4", Age=30 }
};
// 1. 按年龄升序排序(降序用 OrderByDescending)
var sorted = p.OrderBy(i => i.Age);
Console.WriteLine("按年龄升序:");
foreach (var item in sorted)
{
Console.WriteLine($"{item.Name} - {item.Age}岁");
}
// 2. 获取第一个年龄>20的人(无则返回null,避免异常)
var firstAdult = p.FirstOrDefault(i => i.Age > 20);
Console.WriteLine("\n第一个成年人:" + firstAdult?.Name);
// 3. 获取年龄最大值(对象集合需指定属性)
var maxAge = p.Max(i => i.Age);
Console.WriteLine("最大年龄:" + maxAge);
Console.ReadKey();
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
运行结果:
按年龄升序:
chen - 12岁
chen3 - 12岁
chen2 - 24岁
chen4 - 30岁
第一个成年人:chen2
最大年龄:30
新手解析:多个扩展方法可链式调用(如 p.Where(...).OrderBy(...).FirstOrDefault()),是 EF 查询的核心写法。
四、集合常用扩展方法(Lambda 高频用法·新手速查)
整理 EF/日常开发中最常用的扩展方法,附 Lambda 示例和避坑提示:
| 方法 | 核心作用 | Lambda 示例 | 新手避坑提示 |
|---|---|---|---|
| Where | 过滤元素 | list.Where(i => i > 10) | 返回 IEnumerable,延迟执行(遍历才生效) |
| Select | 转换元素 | list.Select(i => i * 2) | 生成新集合,原集合完全不变 |
| Max/Min | 最大/最小值 | p.Max(i => i.Age) | 空集合调用会抛异常,建议先判空 |
| OrderBy/OrderByDescending | 升序/降序排序 | p.OrderBy(i => i.Age) | 多字段排序用 ThenBy |
| First | 获取第一个匹配元素 | list.First(i => i > 5) | 无匹配元素 → 抛异常(新手慎用) |
| FirstOrDefault | 获取第一个匹配元素 | list.FirstOrDefault(i => i > 5) | 无匹配 → 返回默认值,推荐使用 |
| Single | 获取唯一匹配元素 | list.Single(i => i == 10) | 0个/多个匹配 → 抛异常 |
| SingleOrDefault | 获取唯一匹配元素 | list.SingleOrDefault(i => i == 10) | 多个匹配 → 抛异常;0个 → 返回默认值 |
| ToList/ToArray | 类型转换 | p.ToList() / p.ToArray() | 立即执行,EF 中需加 ToList() 才会执行查询 |
五、新手避坑指南(Lambda 高频错误)
- 委托类型不匹配:
Action<string>不能接收int参数; - 多行有返回值未加 return:
// ❌ 错误:多行必须手动 return Func<int, int> add = (a) => { a + 1; }; // ✅ 正确 Func<int, int> add = (a) => { return a + 1; }; - EF 中使用自定义方法:EF 无法解析自定义方法为 SQL,只能用内置方法;
- First/Single 空值异常:优先使用
FirstOrDefault/SingleOrDefault避免报错。
总结
- Lambda 核心规则:单参数可省括号,单行有返回值可省
{}和return,编译器自动推断参数类型; - 核心场景:无返回值(Action)用于“执行操作”,有返回值(Func)用于“计算/查询”;
- EF 关键要点:Lambda 会被解析为 SQL,需使用 EF 支持的方法,加
ToList()等方法才执行查询; - 避坑核心:委托类型匹配、多行有返回值加
return、优先用FirstOrDefault避免异常。
掌握这些规则和示例,你就能在 C# 集合操作、EF 数据查询中灵活使用 Lambda,大幅简化代码并提升开发效率!
👋 关注我!持续分享 C# 实战技巧、代码示例 & 技术干货
-
获取示例代码,轻松上手!
-
私信输入数字: 9qfu19
-
获取代码下载链接