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