1. 委托(Delegate)的本质
1.1 委托是什么?
-
定义:委托是类型安全的函数指针,用于封装具有特定签名的方法。
-
核心特点:
-
可指向任何签名匹配的方法(静态方法或实例方法)
-
支持多播(通过
+=
/-=
添加多个方法) -
是.NET事件机制的基础
-
1.2 委托的声明与使用
// 声明委托类型
public delegate int MathOperation(int a, int b);
class Program
{
static int Add(int a, int b) => a + b;
static int Multiply(int a,int b) => a * b;
static void Main()
{
// 委托实例化
MathOperation op = Add;
Console.WriteLine(op(2, 3)); // 输出 5
// 多播委托
op += Multiply;
Console.WriteLine(op(2, 3)); // 输出 5(最后一个方法的返回值)
}
}
1.3 内置通用委托
-
Action
:无返回值的委托(最多16个参数) -
Func
:有返回值的委托(最后一个泛型参数是返回值类型) -
Predicate<T>
:返回bool的委托(常用于集合筛选)
// 使用内置委托
Func<int, int, int> addDelegate = (x, y) => x + y;
Action<string> logDelegate = msg => Console.WriteLine(msg);
2.2 Lambda的两种形式
-
表达式Lambda(单行代码)
Func<int, bool> isEven = n => n % 2 == 0;
-
语句Lambda(多行代码,用
{}
包裹)Action<string> print = msg => { Console.WriteLine($"Log: {msg}"); File.WriteAllText("log.txt", msg); };
2.3 闭包(Closure)
-
现象:Lambda可以捕获外部变量(包括局部变量、参数、甚至this)
-
本质:编译器生成匿名类来包装捕获的变量
void ClosureDemo()
{
int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 3; // 闭包捕获的是变量的引用
Console.WriteLine(multiplier(5)); // 输出15
}
3. 委托与Lambda的高级应用
3.1 事件驱动编程
public class FileWatcher
{
// 定义事件(基于EventHandler委托)
public event EventHandler<FileEventArgs> FileChanged;
public void Start()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Created += (sender, e) =>
FileChanged?.Invoke(this, new FileEventArgs(e.FullPath));
}
}
// 使用
var watcher = new FileWatcher();
watcher.FileChanged += (sender, e) => Console.WriteLine($"File created: {e.FilePath}");
3.2 LINQ的基石
var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Where方法接收Func<int, bool>委托
var evenNumbers = numbers.Where(n => n % 2 == 0);
// 自定义LINQ操作符
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var item in source)
{
if (predicate(item)) yield return item;
}
}
3.3 回调机制
// 异步操作完成回调
public void DownloadFile(string url, Action<string> onCompleted)
{
Task.Run(() =>
{
// 模拟下载
Thread.Sleep(1000);
onCompleted?.Invoke($"Downloaded: {url}");
});
}
// 使用
DownloadFile("http://example.com", result => Console.WriteLine(result));
4. 表达式树(Expression Trees)
4.1 将Lambda转换为数据
-
表达式树:将Lambda表达式编译成可遍历的树形数据结构
-
应用场景:Entity Framework的LINQ to SQL转换
-
// 将Lambda转换为表达式树 Expression<Func<int, bool>> expr = num => num > 5; // 解析表达式树 var param = expr.Parameters[0]; // num var operation = expr.Body as BinaryExpression; // > var right = operation.Right as ConstantExpression; //
4.2 动态生成Lambda
// 动态构建 num => num > 5
ParameterExpression param = Expression.Parameter(typeof(int), "num");
ConstantExpression constant = Expression.Constant(5);
BinaryExpression comparison = Expression.GreaterThan(param, constant);
Expression<Func<int, bool>> lambda =
Expression.Lambda<Func<int, bool>>(comparison, param);
// 编译并执行
Func<int, bool> func = lambda.Compile();
Console.WriteLine(func(10)); // 输出True
5. 实战技巧与陷阱
5.1 委托与内存泄漏
-
问题:事件订阅导致对象无法被GC回收
-
解决方案:
// 弱事件模式 public class WeakEvent<TEventArgs> { private List<WeakReference<EventHandler<TEventArgs>>> _handlers = new List<WeakReference<EventHandler<TEventArgs>>>(); public void AddHandler(EventHandler<TEventArgs> handler) { _handlers.Add(new WeakReference<EventHandler<TEventArgs>>(handler)); } public void Raise(object sender, TEventArgs e) { foreach (var weakRef in _handlers.ToList()) { if (weakRef.TryGetTarget(out var handler)) handler(sender, e); else _handlers.Remove(weakRef); } } }
5.2 异步Lambda
// 异步事件处理
button.Click += async (sender, e) =>
{
await Task.Delay(1000);
MessageBox.Show("Clicked!");
};
5.3 性能优化
-
避免重复编译表达式树:缓存已编译的委托
-
慎用闭包:在循环中捕获变量可能导致意外结果