函数:委托

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): asyncawait 语法糖的背后,其核心机制就是委托。当你启动一个异步任务时,编译器会生成一个状态机,并使用委托来定义任务完成后需要继续执行的代码(回调)。

  • 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 或函数指针 在异步编程中用法相似

关键差异总结

  1. 语法简洁性:C# 委托语法更简洁统一

  2. 类型安全:C# 委托提供更好的类型安全

  3. 多播支持:C# 原生支持多播委托(+=, -=)

  4. 语言集成:C# 委托是语言原生特性,C++ 需要库支持

  5. 事件系统:C# 事件是语言级特性,C++ 需要框架实现

结论 :C# 的委托最接近 C++ 的 std::function,但提供了更完整、更语言级集成的解决方案,特别是在事件处理和异步编程方面。

相关推荐
老前端的功夫2 小时前
前端技术选型的理性之道:构建可量化的ROI评估模型
前端·javascript·人工智能·ubuntu·前端框架
狮子座的男孩2 小时前
js函数高级:04、详解执行上下文与执行上下文栈(变量提升与函数提升、执行上下文、执行上下文栈)及相关面试题
前端·javascript·经验分享·变量提升与函数提升·执行上下文·执行上下文栈·相关面试题
爱学习的程序媛2 小时前
《JavaScript权威指南》核心知识点梳理
开发语言·前端·javascript·ecmascript
乐观主义现代人3 小时前
go 面试
java·前端·javascript
2501_941886863 小时前
多语言微服务架构下的微服务熔断与限流优化实践
javascript
tsumikistep3 小时前
【前后端】Vue 脚手架与前端工程结构入门指南
前端·javascript·vue.js
慧慧吖@5 小时前
关于在本地去模拟生产环境检测页面内容注意事项
前端·javascript·vue.js
黄团团5 小时前
Vue2整合Electron开发桌面级应用以及打包发布(提供Gitee源码)
前端·javascript·vue.js·elementui·electron
东东2336 小时前
GeoJSON 介绍:Web 地图数据的通用语言
前端·javascript·json