1、委托的定义

定义一个委托跟函数差不多,区别在于:
//1, 定义委托需要加上delegate关键字
//2, 委托的定义不需要函数体
2、委托的使用

3、委托存在的意义?
简单来说,委托存在的意义在于它提供了"函数指针"或"方法引用"的能力,但更重要的是,它实现了更高层次的"抽象"和"解耦",让程序具备极强的灵活性和可扩展性。
我们可以从几个层面来理解为什么有了普通函数还需要委托。
1. 核心思想:从"是什么"到"能做什么"
-
普通函数(方法): 关注的是 "是什么" 。它是一段具体的、固定的代码逻辑。当你调用
CalculateArea(width, height)时,你就是在执行计算矩形面积这个特定的操作。 -
委托: 关注的是 "能做什么" 。它定义了一个"角色"或"能力"。比如,它定义了一个"有两个整数参数并返回整数的方法"这种能力。至于这个能力具体由哪个方法来实现,它并不关心。
这种从具体实现到抽象能力的转变,是委托价值的根本。
2. 委托带来的巨大优势(通过比喻和代码理解)
优势一:实现"回调"与"事件驱动"
这是委托最经典、最常见的用途。
比喻:
想象你在网上订餐。你不需要每隔5分钟就给餐厅打电话问"我的饭好了吗?"。你只需要留下你的电话号码(一个方法的"引用")。当饭做好时,餐厅会 回调 你这个电话(调用你的方法)。这个"电话号码"就是一个委托。
代码示例(C#):
// 1. 定义一个委托(相当于定义了“能接电话”的规范)
public delegate void OrderReadyCallback(string orderId);
// 2. 餐厅类
public class Restaurant
{
// 3. 餐厅有一个接收回调方法的地方(一个接收电话号码的登记本)
public OrderReadyCallback OnOrderReady;
public void PrepareFood(string orderId)
{
// ... 模拟准备食物的耗时过程 ...
Console.WriteLine($"订单 {orderId} 的餐食制作完毕!");
// 4. 餐食做好后,检查是否有人留下了“电话号码”(回调方法)
if (OnOrderReady != null)
{
// 5. 调用回调方法(打电话通知顾客)
OnOrderReady(orderId);
}
}
}
// 6. 顾客类
public class Customer
{
public void Eat(string orderId)
{
Console.WriteLine($"顾客开始享用订单 {orderId} 的餐食!");
}
}
// 7. 使用
class Program
{
static void Main()
{
var restaurant = new Restaurant();
var customer = new Customer();
// 8. 关键一步:将顾客的 Eat 方法“注册”到餐厅的回调上(留下电话号码)
restaurant.OnOrderReady = customer.Eat;
// 9. 开始制作食物
restaurant.PrepareFood("ORDER_123");
// 输出:
// 订单 ORDER_123 的餐食制作完毕!
// 顾客开始享用订单 ORDER_123 的餐食!
}
}
在这个例子中,Restaurant 类完全不知道 Customer 类的存在,也不知道 Eat 方法的具体实现。它只依赖于一个抽象的 OrderReadyCallback 委托。这种 解耦 使得餐厅可以和任何提供了"能接电话"方法的对象协作。
C# 中的事件(event) 就是基于委托的封装,提供了更安全、更标准化的发布/订阅模型。
优势二:实现"策略模式"与"插件式架构"(将委托 作为函数参数!!)
委托可以让你在运行时动态地改变一个对象的行为。
比喻:
想象一个多功能工具箱,它有一个"主要工具插槽"。你可以随时把螺丝刀、锤子、钳子插进去。工具箱本身(调用方)的接口不变,但它的具体功能(被调用的方法)随着你插入的工具(委托指向的方法)而改变。
代码示例(C#):
// 1. 定义一个计算策略的委托
public delegate int CalculationStrategy(int a, int b);
// 2. 一个使用策略的上下文类
public class CalculatorContext
{
private CalculationStrategy _strategy;
// 3. 允许在运行时设置策略(将委托作为函数 参数!)
public void SetStrategy(CalculationStrategy strategy)
{
_strategy = strategy;
}
public int ExecuteCalculation(int a, int b)
{
if (_strategy == null)
throw new Exception("策略未设置!");
return _strategy(a, b);
}
}
// 4. 各种具体的策略(算法)
public class Strategies
{
public static int Add(int a, int b) => a + b;
public static int Multiply(int a, int b) => a * b;
}
// 5. 使用
class Program
{
static void Main()
{
var context = new CalculatorContext();
// 动态设置为加法策略
context.SetStrategy(Strategies.Add);
Console.WriteLine(context.ExecuteCalculation(5, 3)); // 输出:8
// 动态切换为乘法策略
context.SetStrategy(Strategies.Multiply);
Console.WriteLine(context.ExecuteCalculation(5, 3)); // 输出:15
// 甚至可以动态定义一个策略(使用Lambda表达式)
context.SetStrategy((x, y) => x - y);
Console.WriteLine(context.ExecuteCalculation(5, 3)); // 输出:2
}
}
这种方式极大地提高了代码的灵活性,符合 开闭原则 (对扩展开放,对修改封闭)。要添加新的计算方式,你只需要写一个新的符合委托签名的方法,而无需修改 CalculatorContext 类的任何代码。
也可以参考:C#编程中掌握委托(将方法作为参数传递)-腾讯云开发者社区-腾讯云
优势三:支持"异步编程"和"LINQ"
-
异步编程(Async/Await):
async和await语法糖的背后,其核心机制就是委托。当你启动一个异步任务时,编译器会生成一个状态机,并使用委托来定义任务完成后需要继续执行的代码(回调)。 -
LINQ: LINQ 查询中的
Where,Select,OrderBy等方法都接受委托作为参数。这让你可以传入自定义的过滤、投影和排序逻辑。
// LINQ 中的 Where 方法接受一个 Func<T, bool> 委托
var evenNumbers = numbers.Where(n => n % 2 == 0);
// `n => n % 2 == 0` 就是一个传递给 Where 的委托
总结:委托 vs. 普通函数
| 特性 | 普通函数(方法) | 委托 |
|---|---|---|
| 核心概念 | 具体的代码实现 | 方法的抽象、方法的类型 |
| 耦合度 | 调用方必须知道被调方的具体方法 | 高度解耦,调用方只依赖一个抽象签名 |
| 灵活性 | 编译时绑定,行为固定 | 运行时绑定,行为可动态替换 |
| 主要用途 | 实现具体的业务逻辑 | 回调、事件、策略模式、异步、LINQ |
结论:
委托存在的意义,远不止是"像一个函数"。它是一等公民的函数对象 ,是现代软件设计中实现松耦合、高内聚、可扩展架构的基石。它让代码从"命令式"(告诉计算机每一步怎么做)向"声明式"(告诉计算机我想要什么结果,以及处理规则是什么)转变,从而极大地提升了代码的表达能力和架构质量。
4、委托类似 c++中的什么
1. 最直接的类比:函数指针(Function Pointers)
这是委托最基础的 C++ 对应物,用于指向普通函数。
// C++ 函数指针
#include <iostream>
using namespace std;
// 类似 C# 中的 delegate int Calculation(int a, int b);
int Add(int a, int b) { return a + b; }
int Multiply(int a, int b) { return a * b; }
int main() {
// 定义函数指针(类似委托变量)
int (*calculation)(int, int);
// 指向不同的函数(类似委托赋值)
calculation = Add;
cout << calculation(5, 3) << endl; // 输出 8
calculation = Multiply;
cout << calculation(5, 3) << endl; // 输出 15
return 0;
}
2. 更强大的类比:std::function(C++11)
std::function 更接近 C# 委托的完整功能,可以包装函数、lambda、函数对象等。
#include <iostream>
#include <functional>
using namespace std;
class Calculator {
public:
int InstanceMultiply(int a, int b) {
return a * b;
}
};
int main() {
// std::function 类似 C# 委托,可以包装各种可调用对象
function<int(int, int)> calculation;
// 1. 包装普通函数
calculation = Add;
cout << calculation(5, 3) << endl;
// 2. 包装 lambda 表达式
calculation = [](int a, int b) { return a - b; };
cout << calculation(5, 3) << endl;
// 3. 包装成员函数(需要绑定对象)
Calculator calc;
calculation = bind(&Calculator::InstanceMultiply, &calc, placeholders::_1, placeholders::_2);
cout << calculation(5, 3) << endl;
return 0;
}
3. 面向对象场景的类比:函数对象(Functor)
对于需要保存状态的场景,C++ 中使用函数对象。
#include <iostream>
using namespace std;
// 函数对象,类似一个可以保存状态的委托
class Multiplier {
private:
int factor;
public:
Multiplier(int f) : factor(f) {}
int operator()(int a, int b) {
return (a + b) * factor;
}
};
int main() {
Multiplier timesTwo(2);
Multiplier timesThree(3);
cout << timesTwo(5, 3) << endl; // (5+3)*2 = 16
cout << timesThree(5, 3) << endl; // (5+3)*3 = 24
return 0;
}
4. 事件系统的类比:信号和槽(Signals and Slots)
对于 C# 中的事件系统,C++ 中常用的模式是信号槽机制(如 Qt 框架)。
// 类似 C# 中的事件系统(以 Qt 为例)
#include <QObject>
#include <QDebug>
class Restaurant : public QObject {
Q_OBJECT
signals:
void orderReady(QString orderId); // 类似 C# 事件
};
class Customer : public QObject {
Q_OBJECT
public slots:
void eat(QString orderId) { // 类似 C# 事件处理方法
qDebug() << "顾客开始享用订单" << orderId;
}
};
// 使用
Restaurant restaurant;
Customer customer;
// 连接信号和槽(类似 C# 中的 += 事件订阅)
QObject::connect(&restaurant, &Restaurant::orderReady,
&customer, &Customer::eat);
// 触发事件
emit restaurant.orderReady("ORDER_123");
详细对比表格
| C# 委托特性 | C++ 中最接近的对应 | 区别说明 |
|---|---|---|
| 指向普通函数 | 函数指针 | 语法更复杂,类型安全较差 |
| 指向成员函数 | 成员函数指针 | 需要绑定对象实例,使用复杂 |
| 多播委托 | std::vector<std::function> |
需要手动管理调用列表 |
| Lambda 支持 | std::function + lambda |
非常相似,语法略有不同 |
| 事件系统 | 信号槽机制(如 Qt) | 需要框架支持,不是语言原生 |
| 异步回调 | std::function 或函数指针 |
在异步编程中用法相似 |
关键差异总结
-
语法简洁性:C# 委托语法更简洁统一
-
类型安全:C# 委托提供更好的类型安全
-
多播支持:C# 原生支持多播委托(+=, -=)
-
语言集成:C# 委托是语言原生特性,C++ 需要库支持
-
事件系统:C# 事件是语言级特性,C++ 需要框架实现
结论 :C# 的委托最接近 C++ 的 std::function,但提供了更完整、更语言级集成的解决方案,特别是在事件处理和异步编程方面。