一、委托(Delegate):C# 的「函数指针」,方法的容器
1. 核心定义
委托是存储方法引用的类型,可以把方法当作参数传递、赋值、调用,实现解耦 + 回调。
2. 基础语法
// 1. 定义委托(匹配方法的返回值+参数)
public delegate int CalcDelegate(int a, int b);
// 2. 定义匹配委托的方法
public static int Add(int a, int b) => a + b;
public static int Sub(int a, int b) => a - b;
// 3. 使用委托
CalcDelegate calc = Add; // 绑定方法
Console.WriteLine(calc(10, 5)); // 调用:15
calc = Sub; // 重新绑定
Console.WriteLine(calc(10, 5)); // 调用:5
3. 高级用法:多播委托
一个委托绑定多个方法,按顺序执行:
CalcDelegate multiCalc = Add;
multiCalc += Sub; // 追加方法
multiCalc(10, 5); // 依次执行 Add、Sub
multiCalc -= Sub; // 移除方法
4. 系统内置委托(无需自定义,开发必用)
Action:无返回值委托(0~16 个参数)Func<TResult>:有返回值委托(最后一个是返回值)
// 无参数无返回值
Action sayHi = () => Console.WriteLine("Hi");
// 2个参数无返回值
Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);
// 2个参数+int返回值(等价于上面的CalcDelegate)
Func<int, int, int> calcFunc = (a, b) => a * b;
5. 核心价值
- 实现方法回调(异步、事件驱动)
- 解耦代码:调用方不关心具体方法,只关心委托签名
二、事件(Event):委托的「安全封装」,发布 – 订阅模式
1. 核心定义
事件是加了
event 关键字的委托,限制外部直接赋值 / 调用,只允许
+= 订阅、
-= 取消订阅,是安全的委托。
2. 经典场景:按钮点击事件
// 发布者:定义事件
public class Button
{
// 1. 定义事件(基于Action委托)
public event Action OnClick;
// 触发事件(只能在类内部调用)
public void Click()
{
Console.WriteLine("按钮被点击了!");
OnClick?.Invoke(); // 空值触发,避免空引用
}
}
// 订阅者:绑定事件逻辑
public class Program
{
public static void Main()
{
Button btn = new Button();
// 订阅事件(只能+=,不能直接=赋值)
btn.OnClick += ShowMessage;
btn.OnClick += () => Console.WriteLine("Lambda订阅");
btn.Click(); // 触发:依次执行所有订阅方法
}
static void ShowMessage() => Console.WriteLine("弹出提示框");
}
3. 事件 vs 普通委托
表格
| 特性 | 普通委托 | 事件(event) |
|---|---|---|
| 外部赋值 | 允许 del = Method |
禁止,编译报错 |
| 外部调用 | 允许直接调用 | 禁止,只能内部触发 |
| 使用场景 | 通用方法引用 | 发布 – 订阅、UI 事件、回调 |
4. 高级:带参数的标准事件(.NET 规范)
// 自定义事件参数
public class MouseArgs : EventArgs
{
public int X { get; set; }
public int Y { get; set; }
}
public class Button
{
// .NET标准事件:EventHandler<T>
public event EventHandler<MouseArgs> OnMouseMove;
public void Move(int x, int y)
{
OnMouseMove?.Invoke(this, new MouseArgs { X = x, Y = y });
}
}
三、Lambda 表达式:委托的「极简写法」,匿名方法
1. 核心定义
Lambda 是匿名方法的语法糖,用来快速创建委托 / 函数对象,简化代码。
2. 三种写法(从繁到简)
// 1. 原始匿名方法(C#2.0)
Func<int, int, int> add1 = delegate(int a, int b) { return a + b; };
// 2. Lambda 完整写法
Func<int, int, int> add2 = (int a, int b) => { return a + b; };
// 3. Lambda 极简写法(开发最常用)
Func<int, int, int> add3 = (a, b) => a + b;
3. 高级特性:闭包(Closure)
Lambda 可以捕获外部变量,延长变量生命周期:
int factor = 2;
// 捕获外部变量 factor
Func<int, int> multiply = x => x * factor;
Console.WriteLine(multiply(5)); // 10
factor = 3;
Console.WriteLine(multiply(5)); // 15(变量同步更新)
️ 注意:闭包会捕获变量引用,不是值,循环中使用需谨慎。
四、LINQ 高级:数据查询的「瑞士军刀」
LINQ = Language Integrated Query,基于委托 + Lambda,统一查询所有数据(集合、数据库、XML 等)。
1. 两种写法
- 查询语法(类似 SQL,可读性高)
- 方法语法(链式调用,灵活强大,高级开发首选)
2. 基础准备:测试数据
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string ClassName { get; set; }
}
// 测试集合
List<Student> students = new List<Student>
{
new Student{Id=1, Name="张三", Age=18, ClassName="一班"},
new Student{Id=2, Name="李四", Age=20, ClassName="二班"},
new Student{Id=3, Name="王五", Age=18, ClassName="一班"},
new Student{Id=4, Name="赵六", Age=22, ClassName="二班"}
};
3. LINQ 高级核心操作
(1)筛选 + 投影(Where + Select)
// 方法语法:查询一班、年龄18岁的学生姓名
var query = students
.Where(s => s.ClassName == "一班" && s.Age == 18)
.Select(s => new { s.Name, s.Age }); // 匿名对象
(2)排序(OrderBy / ThenBy)
// 先按年龄升序,年龄相同按姓名降序
var sorted = students
.OrderBy(s => s.Age)
.ThenByDescending(s => s.Name);
(3)分组(GroupBy)
// 按班级分组,统计每组人数
var groupByClass = students
.GroupBy(s => s.ClassName)
.Select(g => new
{
Class = g.Key,
Count = g.Count(),
AvgAge = g.Average(s => s.Age)
});
(4)去重、限制、跳过(Distinct / Take / Skip)
// 分页:跳过2条,取2条
var pageData = students.Skip(2).Take(2);
// 年龄去重
var ages = students.Select(s => s.Age).Distinct();
(5)聚合函数(Count / Sum / Max / Min / Average)
int total = students.Count();
int maxAge = students.Max(s => s.Age);
double avgAge = students.Average(s => s.Age);
(6)Any / All / Contains(判断)
// 是否存在年龄>20的学生
bool hasAdult = students.Any(s => s.Age > 20);
// 是否所有学生都大于18岁
bool allAdult = students.All(s => s.Age >= 18);
(7)Join 关联查询(多集合联合)
// 模拟班级集合
List<Class> classes = new List<Class>
{
new Class{Name="一班", Teacher="张老师"},
new Class{Name="二班", Teacher="李老师"}
};
// 学生+班级关联查询
var joinQuery = students
.Join(classes,
s => s.ClassName, // 学生关联字段
c => c.Name, // 班级关联字段
(s, c) => new { s.Name, s.Age, c.Teacher }); // 结果
4. LINQ 高级:延迟执行(核心特性)
LINQ 查询不会立即执行,只有在遍历数据(
foreach/
ToList()/
Count())时才执行:
// 仅定义查询,不执行
var query = students.Where(s => s.Age > 18);
students.Add(new Student{Name="钱七", Age=21}); // 新增数据
// 执行时会包含新增的数据!
foreach (var item in query) { }
优点:性能高,按需执行;️ 注意:多次遍历会重复查询。
五、四者关系(终极总结,秒懂)
- 委托:地基,存储方法的容器
- 事件:委托的安全版,专门做发布 – 订阅
- Lambda:委托的极简写法,快速创建方法
- LINQ:基于委托 + Lambda,实现数据链式查询
LINQ 用 Lambda 简化委托调用,委托是方法的引用,事件是安全的委托。
总结
- 委托:方法引用,
Action/Func是开发标配; - 事件:
event修饰委托,安全的发布 – 订阅模式; - Lambda:匿名方法语法糖,支持闭包,简化委托;
- LINQ:链式查询神器,延迟执行,支持筛选 / 分组 / 关联 / 聚合。
文章摘自:https://www.cnblogs.com/chuansheng/p/19908138
