Lambda表达式
Lambda表达式概述
Lambda表达式是一种简洁的匿名函数语法,可以用更少的代码定义内联函数。它是C#函数式编程的核心特性,也是LINQ查询的基础。
Lambda语法
Lambda表达式的基本语法为:参数 => 表达式或语句块。=>读作"goes to",将左侧参数映射到右侧的操作。
示例
CSHARP
using System;
class Program
{
static void Main()
{
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
}
}
TEXT
25
单行Lambda与多行Lambda
单行Lambda直接返回表达式的结果,无需return关键字和花括号。多行Lambda需要花括号包裹语句块,并显式使用return返回。
示例
CSHARP
using System;
class Program
{
static void Main()
{
Func<int, int> singleLine = x => x * x;
Func<int, int> multiLine = x =>
{
int result = x * x;
return result;
};
Console.WriteLine(singleLine(4));
Console.WriteLine(multiLine(4));
}
}
TEXT
16
16
类型推断
编译器通常可以根据上下文推断Lambda参数的类型,无需显式声明。当上下文不够明确时,可以手动指定类型。
示例
CSHARP
using System;
class Program
{
static void Main()
{
Func<int, int> inferred = x => x + 1;
Func<int, int> explicitType = (int x) => x + 1;
Console.WriteLine(inferred(10));
Console.WriteLine(explicitType(10));
}
}
TEXT
11
11
Lambda与委托
Lambda表达式可以赋值给委托类型,这使它成为创建委托实例最简洁的方式。相比传统的匿名方法或显式委托实例化,Lambda更简短。
示例
CSHARP
using System;
delegate int Transform(int x);
class Program
{
static void Main()
{
Transform doubleIt = x => x * 2;
Transform addTen = x => x + 10;
Console.WriteLine(doubleIt(3));
Console.WriteLine(addTen(3));
}
}
TEXT
6
13
Lambda与Func和Action
Func<T, TResult>表示有返回值的泛型委托,Action<T>表示无返回值的泛型委托。Lambda与它们配合使用非常常见。
示例
CSHARP
using System;
class Program
{
static void Main()
{
Func<int, int, int> add = (a, b) => a + b;
Action<string> greet = msg => Console.WriteLine(msg);
Console.WriteLine(add(3, 7));
greet("Hello");
}
}
TEXT
10
Hello
Lambda与Predicate
Predicate<T>是返回bool的委托,常用于过滤和判断条件。Lambda表达式让它定义条件变得极其简洁。
示例
CSHARP
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
Predicate<int> isEven = x => x % 2 == 0;
var evens = numbers.FindAll(isEven);
Console.WriteLine(string.Join(", ", evens));
}
}
TEXT
2, 4, 6
闭包
Lambda表达式可以捕获外部变量,这称为闭包。被捕获的变量生命周期会随Lambda延长,即使原始作用域已结束,变量依然存活。
示例
CSHARP
using System;
class Program
{
static void Main()
{
int factor = 3;
Func<int, int> multiply = x => x * factor;
Console.WriteLine(multiply(5));
factor = 10;
Console.WriteLine(multiply(5));
}
}
TEXT
15
50
💡 闭包捕获的是变量本身而非变量的值,因此外部变量修改会影响Lambda的执行结果。
示例
CSHARP
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
foreach (var action in actions)
{
action();
}
}
}
TEXT
0
1
2
⚠️ 如果直接捕获循环变量
i而不创建副本,所有Lambda将共享同一个i,最终输出均为3。创建局部副本可以避免此问题。
Lambda在LINQ中的应用铺垫
Lambda表达式是LINQ查询的核心。许多LINQ方法接受Func或Expression参数,Lambda提供了内联编写条件、投影和排序逻辑的简洁方式。
示例
CSHARP
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
var numbers = new List<int> { 3, 7, 1, 9, 4, 6 };
var big = numbers.Where(x => x > 5);
var doubled = numbers.Select(x => x * 2);
var sorted = numbers.OrderBy(x => x);
Console.WriteLine(string.Join(", ", big));
Console.WriteLine(string.Join(", ", doubled));
Console.WriteLine(string.Join(", ", sorted));
}
}
TEXT
7, 9, 6
6, 14, 2, 18, 8, 12
1, 3, 4, 6, 7, 9
表达式树简介
将Lambda赋值给Expression<TDelegate>时,编译器会生成一个数据结构(表达式树)而非可执行代码。表达式树可以被解析、修改和转换为其他形式,例如SQL查询。这是LINQ to SQL等技术的底层原理。
示例
CSHARP
using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
Expression<Func<int, int>> expr = x => x * x + 1;
Console.WriteLine(expr.Body);
Console.WriteLine(expr.Parameters[0]);
var compiled = expr.Compile();
Console.WriteLine(compiled(4));
}
}
TEXT
(x * x) + 1
x
17
ℹ️
Expression<Func<int,int>>与Func<int,int>类型不同,前者是数据结构,后者是可执行委托。
❓ 常见问题
Q Lambda和匿名方法有什么区别?
A Lambda语法更简洁,支持类型推断和表达式树,匿名方法语法冗长且功能较少。
Q:Lambda参数什么时候必须显式指定类型? A:当编译器无法从上下文推断类型时,例如参数类型不明确或重载歧义时。
Q:闭包捕获的变量在什么时候释放? A:当所有引用该变量的Lambda都被垃圾回收后,捕获的变量才会释放。
Q:Expression和Func可以互相转换吗? A:Expression可以通过Compile()转为Func,但Func不能直接转回Expression。
📖 小节
- Lambda语法为
参数 => 表达式,简洁地定义匿名函数 - 单行Lambda省略花括号和return,多行Lambda需要两者
- 编译器可推断Lambda参数类型,必要时可显式声明
- Lambda可直接赋值给自定义委托、Func、Action和Predicate
- 闭包让Lambda捕获外部变量,变量生命周期随之延长
- 循环中捕获变量需注意创建副本以避免意外共享
- Lambda是LINQ方法的核心参数形式
- 表达式树将Lambda表示为数据结构,可用于运行时分析和转换
📝 作业
- 编写一个
Func<double, double, double>的Lambda,计算两个数的最大值 - 使用
Action<string>的Lambda,输出一条带时间戳的日志信息 - 编写闭包示例:创建一个计数器函数,每次调用返回递增的整数
- 使用
Where和Lambda从字符串列表中筛选出长度大于5的字符串 - 将一个
Func<int, bool>的Lambda分别赋值给Func<int, bool>和Expression<Func<int, bool>>,分别输出它们的GetType()结果



