====== C# 委托 (Delegate) 深度解析 ======
委托是 C# 中一种非常重要的引用类型,它是函数式编程在 C# 中的基础。
===== 1. 简介 =====
**关键字**:%%delegate%% (读音: /'dɛlɪɡət/)
**核心概念**:
* 委托将**方法(函数)当作变量**进行处理。
* 委托是一种**引用类型**,它定义了方法的签名(Signature)。
* 只要方法的签名(返回值类型 + 参数列表)与委托定义一致,该方法就可以赋值给委托变量。
**通俗理解**:委托就像是一个“办事处代表”。你定义了这个代表能办什么事(比如能接收两个整数,返回一个整数)。任何符合这个标准的具体办事员(具体方法),都可以被指派给这个代表去执行任务。
===== 2. 委托的使用步骤 =====
使用自定义委托通常分为三步:
- **定义 (Define)**:像定义类一样,在类外部(或内部)声明委托类型。
- **实例化 (Instantiate)**:创建委托对象,并指向一个具体的方法。
- **调用 (Invoke)**:像调用函数一样调用委托对象。
// 1. 声明委托 (规定了必须是:两个int参数,返回bool)
public delegate bool CompareDelegate(int a, int b);
class Program
{
static void Main()
{
// 2. 实例化委托,指向 IsGreater 方法
// 注意:这里只写方法名,不要带括号()
CompareDelegate compare = IsGreater;
// 3. 调用委托
bool result = compare(5, 3);
Console.WriteLine($"Is 5 greater than 3? {result}");
}
// 具体方法:签名必须与委托一致
static bool IsGreater(int a, int b)
{
return a > b;
}
}
===== 3. 委托应用场景举例 =====
==== 案例 1:逻辑解耦 (字符串过滤器) ====
委托允许我们将判断逻辑从主流程中剥离出来。
using System;
// 定义委托:接收 string,返回 bool
public delegate bool StringFilter(string input);
class Program
{
static void Main()
{
// 这里的逻辑是可以动态替换的
StringFilter filter = IsLongEnough;
bool result = filter("Hello, World!");
Console.WriteLine($"Is 'Hello, World!' long enough? {result}");
}
static bool IsLongEnough(string input)
{
return input.Length >= 10;
}
}
==== 案例 2:跨类调用 ====
如果委托需要指向另一个类的方法,需要先实例化那个类。
class Program
{
// 自定义委托类型
public delegate int MathOpDelegate(int a, int b);
static void Main(string[] args)
{
// 1. 实例化包含方法的类
Calculator calc = new Calculator();
// 2. 委托指向该实例的成员方法
MathOpDelegate myDelegate = calc.Multiply; // 注意:不要加括号()
// 3. 执行委托
int result = myDelegate(5, 7);
Console.WriteLine($"结果: {result}"); // 输出 35
}
}
class Calculator
{
public int Multiply(int a, int b)
{
return a * b;
}
}
===== 4. 系统预定义委托 (Action & Func) =====
为了避免每次使用委托都要先写 `public delegate ...`,C# 提供了两个通用的泛型委托。
^ 委托类型 ^ 返回值 ^ 用途 ^ 格式示例 ^
| **Action** | **无 (void)** | 执行动作,不需结果 | %%Action%% |
| **Func** | **有 (TResult)** | 计算并返回结果 | %%Func%% |
==== Action 委托 (无返回值) ====
`Action` 后面尖括号里的类型全是**输入参数**。
static void Main()
{
// Action 表示:接收一个 string 参数,无返回值
Action print = PrintMessage;
print("Hello, System Delegate!");
}
static void PrintMessage(string message)
{
Console.WriteLine(message);
}
==== Func 委托 (有返回值) ====
`Func` 后面尖括号里,**最后一个类型永远是返回值类型**,前面的都是输入参数类型。
static void Main()
{
// Func 表示:
// 输入参数1: int
// 输入参数2: int
// 返回值: int
Func addFunc = Add;
int sum = addFunc(3, 4);
Console.WriteLine($"3 + 4 = {sum}");
}
static int Add(int a, int b)
{
return a + b;
}
===== 5. 匿名方法与 Lambda 表达式 =====
有时我们只需要一个简单的逻辑,专门写一个静态函数太麻烦,这时可以使用匿名方法。
==== 匿名方法 (C# 2.0 写法) ====
在实例化委托时,直接使用 `delegate` 关键字定义方法体。
public delegate int MathDelegate(int a, int b);
static void Main(string[] args)
{
// 直接内联定义逻辑,没有方法名
MathDelegate multiply = delegate (int a, int b)
{
return a * b;
};
int result = multiply(5, 7);
Console.WriteLine(result);
}
==== 知识拓展:Lambda 表达式 (C# 3.0+ 推荐写法) ====
现代 C# 开发中,几乎不再使用 `delegate (...) {}` 的匿名方法写法,而是使用更简洁的 **Lambda 表达式** (`=>`)。
static void Main(string[] args)
{
// 这里的 (a, b) => a * b 等同于上面的匿名方法
// => 读作 "goes to"
Func multiply = (a, b) => a * b;
int result = multiply(5, 7);
Console.WriteLine(result);
}
===== 总结 =====
- **定义**:委托是方法的容器,可以把方法当参数传递。
- **自定义**:使用 `delegate` 关键字定义签名。
- **系统内置**:
- `Action`: 无返回值。
- `Func`: 有返回值(最后一个泛型参数是返回类型)。
- **进化**:从 `自定义委托` -> `Action/Func` -> `匿名方法` -> `Lambda表达式`,代码越来越简洁。