在 C# 中,**委托(Delegate)**是一个核心且灵活的特性。它本质上是 类型安全的函数指针 ,允许将方法作为参数传递给其他方法。委托在 事件处理、回调函数、LINQ 查询 等场景中非常常用。
本文将从基础概念、声明与实例化、到多播委托、事件及常用委托类型进行全面讲解,并结合实战示例帮助你快速掌握。
1. 什么是委托?为什么它比函数指针更安全?
委托是一种 引用类型,用于存储对某个方法的引用。它类似于 C/C++ 的函数指针,但比函数指针更安全:
- 类型安全:只有与委托签名匹配的方法才能赋值给委托。
- 动态调用:可以在运行时改变委托引用的方法。
- 广泛应用:事件处理、回调函数、LINQ 查询等。
💡 Tip :所有委托类型都继承自 System.Delegate。
场景示例 :在一个算法方法中,根据不同条件动态选择处理方法,委托可以让你轻松传递方法,而无需复杂的 if/else 分支。
2. 如何声明委托?
声明委托的语法如下:
csharp
public delegate <返回类型> <委托名>(<参数列表>);
示例:
csharp
// 接受两个整数并返回整数
public delegate int MathOperation(int x, int y);
// 接受一个字符串并返回整数
public delegate int MyDelegate(string s);
⚠️ 注意 :委托声明定义了可引用的方法签名,签名不匹配将无法赋值。
3. 实例化与调用委托
创建委托对象通常使用 new 关键字,并绑定具体方法:
csharp
public delegate void PrintString(string s);
PrintString ps1 = new PrintString(WriteToScreen);
PrintString ps2 = new PrintString(WriteToFile);
示例:简单委托调用
csharp
using System;
delegate int NumberChanger(int n);
class Program
{
static int num = 10;
public static int AddNum(int p) => num += p;
public static int MultNum(int q) => num *= q;
public static int GetNum() => num;
static void Main()
{
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc1(25);
Console.WriteLine("Value of Num: " + GetNum()); // 输出 35
nc2(5);
Console.WriteLine("Value of Num: " + GetNum()); // 输出 175
}
}
🔍 解析:
nc1调用AddNum,累加 25。nc2调用MultNum,乘以 5。- 委托实现了方法动态调用。
4. 多播委托(Multicast Delegate)
多播委托可以让一个委托引用多个方法,通过 + 合并,使用 - 移除:
csharp
NumberChanger nc = nc1 + nc2;
nc(5); // 先执行 AddNum,再执行 MultNum
Console.WriteLine("Value of Num: " + GetNum()); // 75
移除方法示例:
csharp
PrintMessage print = PrintUpperCase + PrintLowerCase;
print -= PrintLowerCase; // 只剩 PrintUpperCase
print("Hello, C#"); // 输出 HELLO, C#
多播委托执行顺序示意
nc = nc1 + nc2
调用 nc(5) ->
┌─────────┐
│ AddNum │ num += 5
└─────────┘
┌─────────┐
│ MultNum │ num *= 5
└─────────┘
💡 Tip:多播委托常用于事件触发,确保所有订阅方法按顺序执行。
5. 委托的常见用途
5.1 回调函数
委托可以作为方法参数,实现"回调机制":
csharp
public static void SendString(PrintString ps)
{
ps("Hello World");
}
5.2 实战:打印字符串到控制台或文件
csharp
public delegate void PrintString(string s);
public static void WriteToScreen(string str) => Console.WriteLine(str);
public static void WriteToFile(string s)
{
using var sw = new StreamWriter("c:\\message.txt", true);
sw.WriteLine(s);
}
PrintString ps1 = WriteToScreen;
PrintString ps2 = WriteToFile;
SendString(ps1); // 控制台输出
SendString(ps2); // 写入文件
🔍 解析 :SendString 可以接受任意符合签名的委托,实现逻辑可插拔。
6. 委托与事件
事件是封装了委托的机制,实现发布-订阅模式:
csharp
public class Button
{
public event EventHandler Click;
public void OnClick() => Click?.Invoke(this, EventArgs.Empty);
}
var button = new Button();
button.Click += (s, e) => Console.WriteLine("Button clicked!");
button.OnClick(); // 输出 "Button clicked!"
事件机制示意
Button.OnClick() ->
┌─────────────┐
│ Click事件链 │
└─────────────┘
↓
订阅方法逐一执行
💡 Tip:事件只能订阅或取消订阅,不能被外部直接调用,保证封装性。
7. 常用委托类型
| 委托类型 | 功能 | 示例 |
|---|---|---|
| Action | 无返回值 | Action<string> print = Console.WriteLine; |
| Func | 有返回值 | Func<int,int,int> add = (x,y)=>x+y; |
| Predicate | 返回 bool | Predicate<int> isEven = x=>x%2==0; |
⚠️ 注意:Action/Func 最多支持 16 个参数,Func 的最后一个参数是返回值。
8. 委托使用注意事项
- 类型安全:委托只能引用匹配签名的方法。
- 匿名方法 / Lambda:简化实例化。
csharp
Func<int,int,int> add = (x,y)=>x+y;
Console.WriteLine(add(5,3)); // 8
- 异步调用 :委托可以配合
BeginInvoke/EndInvoke实现异步操作。
9. 总结与实战建议
C# 委托是一个功能强大、灵活且类型安全的机制。掌握委托可以:
- 实现 事件驱动编程
- 实现 回调机制
- 支持 函数式编程风格
- 提高程序模块化和代码复用能力
💡 实战建议:
- 事件处理使用多播委托,保证订阅方法顺序执行。
- 回调函数使用匿名方法或 Lambda 简化代码。
- 对于复杂场景,可结合 Action/Func/Predicate 提高代码可读性。
📌 本文收获:
- 理解委托概念及类型安全特性
- 掌握委托的声明、实例化、调用及多播
- 了解事件与委托的关系
- 熟悉常用委托类型:Action、Func、Predicate
- 提高代码复用与模块化能力
💡 文章亮点:
- 代码示例丰富,每段代码都有解析
- 多播委托与事件机制用 ASCII 图示帮助理解
- 强调实战场景,贴合开发者使用习惯
- 小技巧提示框(Tip/注意)增强可读性