C#委托、事件和lambda表达式
委托委托就像是方法的类型有了委托就可以把方法当参数传来传去或者在一个变量里随时换掉要执行的方法。// 1. 声明一个委托类型定义我能代表哪种方法 public delegate void MyDelegate(string msg); // 2. 写一个符合签名的方法 public static void ShowMessage(string text) { Console.WriteLine($消息{text}); } // 3. 使用 MyDelegate del ShowMessage; // 把方法赋值给委托变量 del(Hello); // 调用委托相当于调用 ShowMessage大多数情况下不需要自己声明委托直接用系统自带的就行。// Action 示例无返回值 Actionstring print s Console.WriteLine(s); print(直接用 Action); // Func 示例有返回值 Funcint, int, int add (a, b) a b; int result add(3, 4); // 7委托可以串联多个方法调用一次就会按顺序执行全部也就是多播。Action act MethodA; act MethodB; act MethodC; act(); // 依次执行 A、B、C // 移除方法 act - MethodB;事件事件就是加了限制的委托比如只能从外部“订阅”和“取消订阅”不能直接触发或覆盖。相当于给委托封装了一下。下面举个例子// 定义一个事件发布者 public class Button { // 事件外部可以订阅但不能随意触发 public event EventHandler? Clicked; // 模拟点击按钮 public void Click() { Console.WriteLine(按钮被点击了); // 触发事件只能在类内部触发 Clicked?.Invoke(this, EventArgs.Empty); } } // 使用 class Program { static void Main() { Button btn new Button(); // 订阅事件用 btn.Clicked OnButtonClicked; // 触发点击 btn.Click(); // 取消订阅用 - btn.Clicked - OnButtonClicked; // btn.Clicked.Invoke(null, null); ❌ 编译错误外部不能触发 // btn.Clicked null; ❌ 编译错误外部不能赋值 } static void OnButtonClicked(object? sender, EventArgs e) { Console.WriteLine(事件处理按钮被点击了); } }输出是按钮被点击了 事件处理按钮被点击了在.NET中推荐是使用EventHandler和EventHandlerT它有两个参数public event EventHandlerCustomEventArgs? DataReceived;标准写法是public class DataProcessor { // 声明事件使用标准 EventHandlerT public event EventHandlerProcessCompletedEventArgs? ProcessCompleted; public void StartProcess() { // 模拟处理 Console.WriteLine(处理中...); Thread.Sleep(1000); // 处理完成触发事件 OnProcessCompleted(10); // 假设处理了10条数据 } // 受保护的触发方法约定On 事件名 protected virtual void OnProcessCompleted(int count) { ProcessCompleted?.Invoke(this, new ProcessCompletedEventArgs(count)); } } // 自定义事件参数类 public class ProcessCompletedEventArgs : EventArgs { public int RecordCount { get; } public ProcessCompletedEventArgs(int count) RecordCount count; } // 使用 class Program { static void Main() { var processor new DataProcessor(); processor.ProcessCompleted (sender, e) { Console.WriteLine($处理完成共 {e.RecordCount} 条数据); }; processor.StartProcess(); } }Lambda表达式lambda表达式就是匿名方法的简写是C#中最常用最实用的特性之一。传统写法// 定义方法 bool IsEven(int x) { return x % 2 0; } // 使用 Listint numbers new Listint{1,2,3,4,5}; var evens numbers.Where(IsEven);lambda写法var evens numbers.Where(x x % 2 0);最直观的看到lambda写法少写了三行代码显著减少了代码冗余。lambda可以捕获外部变量也就是闭包int multiplier 3; Funcint, int multiply x x * multiplier; // 捕获了 multiplier int result multiply(5); // 15 multiplier 10; result multiply(5); // 50外部变量变化Lambda 也跟着变但在循环中捕获变量时很容易造成“延迟执行”的问题。var actions new ListAction(); for (int i 0; i 3; i) { actions.Add(() Console.WriteLine(i)); // 捕获同一个 i } foreach (var action in actions) { action(); // 输出 3, 3, 3 而不是 0,1,2 }解决方法也很简单就是在循环内局部复制一份for (int i 0; i 3; i) { int copy i; // 每次循环新建一个变量 actions.Add(() Console.WriteLine(copy)); } // 输出 0,1,2一般情况下编译器会根据上下文推断lambda的类型只有上下文不够明确时才需要显示声明类型// ❌ 错误无法推断 var lambda x x * 2; // ✅ 正确明确委托类型 Funcint, int lambda x x * 2;