C# 委托、事件、Lambda、LINQ

一、委托(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) { }

    优点:性能高,按需执行;️ 注意:多次遍历会重复查询。  


 

五、四者关系(终极总结,秒懂)

 

  1. 委托:地基,存储方法的容器
  2. 事件:委托的安全版,专门做发布 – 订阅
  3. Lambda:委托的极简写法,快速创建方法
  4. LINQ:基于委托 + Lambda,实现数据链式查询

  LINQ 用 Lambda 简化委托调用,委托是方法的引用,事件是安全的委托。  


 

总结

 

  1. 委托:方法引用,Action/Func 是开发标配;
  2. 事件:event 修饰委托,安全的发布 – 订阅模式;
  3. Lambda:匿名方法语法糖,支持闭包,简化委托;
  4. LINQ:链式查询神器,延迟执行,支持筛选 / 分组 / 关联 / 聚合。

文章摘自:https://www.cnblogs.com/chuansheng/p/19908138