委托、事件与 Lambda
1. 委托 delegate
委托是类型安全的函数指针:
public delegate int MathOp(int a, int b);
MathOp add = (a, b) => a + b;
MathOp sub = (a, b) => a - b;
Console.WriteLine(add(1, 2)); // 32. 内置委托类型
| 类型 | 形式 |
|---|---|
Action |
无参无返回 |
Action<T> |
一参无返回 |
Action<T1,T2> |
两参无返回 |
Func<TR> |
无参返回 TR |
Func<T,TR> |
一参返回 TR |
Predicate<T> |
一参返回 bool |
Action<string> log = s => Console.WriteLine(s);
Func<int, int, int> mul = (a, b) => a * b;
Predicate<int> isPos = n => n > 0;3. Lambda 表达式
Func<int, int> sq = x => x * x;
Func<int, int, int> add = (a, b) => a + b;
Action<int> print = x => Console.WriteLine(x);
// 语句 lambda
Func<int, int> abs = x =>
{
if (x < 0) return -x;
return x;
};闭包
int factor = 10;
Func<int, int> mul = x => x * factor; // 捕获外部变量
factor = 20;
Console.WriteLine(mul(5)); // 100(按引用捕获)4. 多播委托
Action<string> log = Console.WriteLine;
log += x => File.AppendAllText("a.log", x);
log("hello"); // 输出 + 写文件
log -= ...;5. 事件 event
事件是基于委托的发布订阅模式:
public class Button
{
public event EventHandler Clicked;
public void Click() => Clicked?.Invoke(this, EventArgs.Empty);
}
var btn = new Button();
btn.Clicked += (s, e) => Console.WriteLine("clicked");
btn.Click();自定义事件参数:
public class PriceChangedEventArgs : EventArgs
{
public decimal OldPrice { get; set; }
public decimal NewPrice { get; set; }
}
public event EventHandler<PriceChangedEventArgs> PriceChanged;
PriceChanged += (s, e) =>
Console.WriteLine($"{e.OldPrice} → {e.NewPrice}");6. 表达式树 Expression
using System.Linq.Expressions;
Expression<Func<int, int>> expr = x => x * 2;
var compiled = expr.Compile();
Console.WriteLine(compiled(3)); // 6EF Core 等用表达式树把 C# lambda 翻译为 SQL。
7. 局部函数 vs Lambda
public IEnumerable<int> Get()
{
return Iterator();
IEnumerable<int> Iterator()
{
for (int i = 0; i < 10; i++) yield return i;
}
}局部函数不分配闭包对象,性能略好。
8. Func 与 Action 反射
MethodInfo CallDelegate(MethodInfo m, object target, object[] args)
=> (MethodInfo)m.Invoke(target, args);小结
委托是 C# 一切回调和事件的基础,lambda 让函数式编程风格更自然,表达式树让 ORM 可以把 C# 翻译为 SQL。