C#委托、C++委托实现、C回调函数

2023-11-14

C#委托

C# 中的委托(Delegate)定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用if-else(switch)语句,同时使得程序具有更好的可扩展性。委托特别用于实现事件和回调方法。所有的委托都派生自 System.Delegate 类。

声明委托

        委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。

        语法

修饰符 delegate 返回值类型 委托名 ( 参数列表 );

delegate void IntMethodInvoker(intx);

 定义了一个委托叫做 IntMethodInvoker ,这个委托可以指向带有一个int类型的参数,并且返回值是void的一个方法。

        定义一个委托要定义方法的参数和返回值,使用delegate定义delegate void FirstInvoke(int x);

实例化委托

        在定义好委托后就可以实例化委托,命名方法委托在实例化委托时必须带入方法的具体名称或者是null。

委托名  委托对象名 = new 委托名 ( 方法名 );

 委托数组

 委托名[] 委托对象名 = {委托的方法1,......}; 

委托做完函数的参数 

访问类型 static 返回类型 方法名(委托类型 委托参数,参数.....)  

多播委托

        委托可以包含多个方法,这种委托叫做多播委托。使用多播委托就可以按照顺序调用多个方法,多播委托只能得到调用的最后一个方法的结果,一般我们把多播委托的返回类型声明为void。

Action action1 = Test1;

action2 += Test2;

action2 -= Test1

//获取委托列表

Delegate[] delegate = action1.GetInvocationList();

匿名方法

        不用去定义一个方法,而是在后面直接使用匿名的方法。相当于直接把要引用的方法直接写在了后面,优点是减少了要编写的代码,减少代码的复杂性。

 Func<T> 委托对象名 = delegate(方法参数){

        方法体

};

事件

        事件(Event)是类或者对象向其他类或对象通知发送的事情的一种特殊签名的委托

        事件的声明

public event 委托类型 事件名;

事件使用event关键词来声明,他的返回值是一个委托类型

        通常事件的命名,以名字+Event 作为他的名称,在编码中尽量使用规范命名,增加代码可读性。

 示例代码:

using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // 创建委托实例
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         // 使用委托对象调用方法
         nc1(25);
         Console.WriteLine("Value of Num: {0}", getNum());
         nc2(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

C++ 委托实现

委托实际上就是将任意个同类型的函数交给一个对象(委托对象)来实现,可以想到如果要用C++来实现的话,肯定要用到函数指针。又因为非静态成员对象的调用依赖于具体的实例对象,为了能够以统一的方式存储这些函数指针就需要用多态进行封装。以下代码是利用C++特性实现的委托。

#include <iostream>
#include <list>
using namespace std;

class IDelegate
{
public:
    virtual ~IDelegate() {}
    virtual bool isType(const std::type_info &_type) = 0;
    virtual void invoke() = 0; // 回调函数
    virtual bool compare(IDelegate *_delegate) const = 0;
};

// 全局函数委托类
class CStaticDelegate : public IDelegate
{
public:
    typedef void (*Func)(); // Func是函数指针类型的别名
    CStaticDelegate(Func _func) : mFunc(_func) {}
    // 查看类是否一致
    virtual bool isType(const std::type_info &_type) { return typeid(CStaticDelegate) == _type; }

    virtual void invoke() { mFunc(); } // 通过函数指针调用全局函数

    // 输入委托基类
    virtual bool compare(IDelegate *_delegate) const
    {
        // 判断和自身的类型是否相同
        if (0 == _delegate || !_delegate->isType(typeid(CStaticDelegate)))
            return false;
        // 向下转型,判断函数地址是否相同
        CStaticDelegate *cast = static_cast<CStaticDelegate *>(_delegate);
        return cast->mFunc == mFunc;
    }

private:
    Func mFunc; // 定义私有函数指针
};

// 成员函数委托类
template <class T> // 对于成员函数委托,需要使用模版,因为不能对一个类单独化。
class CMethodDelegate : public IDelegate
{
public:
    typedef void (T::*Method)(); // 这是一个指向 T作用域下的无返回值的无参成员函数 的函数指针

    CMethodDelegate(T *_object, Method _method) : mObject(_object), mMethod(_method) {}

    virtual bool isType(const std::type_info &_type) { return typeid(CMethodDelegate<T>) == _type; }

    virtual void invoke()
    {
        (mObject->*mMethod)();
    }
    virtual bool compare(IDelegate *_delegate) const
    {
        if (0 == _delegate || !_delegate->isType(typeid(CMethodDelegate<T>)))
            return false;
        CMethodDelegate<T> *cast = static_cast<CMethodDelegate<T> *>(_delegate);
        return cast->mObject == mObject && cast->mMethod == mMethod;
    }

private:
    T *mObject;     // 定义调用这个函数的类对象指针,因为调用成员函数需要类对象来调用
    Method mMethod; // 定义函数指针
};

// 委托管理
#if 1
class CMultiDelegate
{
public:
    // 存储委托容器
    typedef list<IDelegate *> ListDelegate;
    // 容器迭代器
    typedef ListDelegate::iterator ListDelegateIterator;
    // 容器const迭代器
    typedef ListDelegate::const_iterator ConstListDelegateIterator;

    CMultiDelegate() {}
    ~CMultiDelegate() { clear(); }

    // 是否为空
    bool empty() const
    {
        for (ConstListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter)
                return false;
        }
        return true;
    }
    // 清空
    void clear()
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if (*iter)
            {
                delete (*iter);
                (*iter) = 0;
            }
        }
    }
    // 添加绑定
    CMultiDelegate &operator+=(IDelegate *_delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate)) // 判断是否已经存在,就不在添加
            {
                delete _delegate;
                return *this;
            }
        }
        mListDelegates.push_back(_delegate);
        return *this;
    }
    // 解开绑定
    CMultiDelegate &operator-=(IDelegate *_delegate)
    {
        for (ListDelegateIterator iter = mListDelegates.begin(); iter != mListDelegates.end(); ++iter)
        {
            if ((*iter) && (*iter)->compare(_delegate))
            {
                if ((*iter) != _delegate)
                    delete (*iter);
                (*iter) = 0;
                break;
            }
        }
        delete _delegate;
        return *this;
    }

    void operator()()
    {
        // 调用
        ListDelegateIterator iter = mListDelegates.begin();
        while (iter != mListDelegates.end())
        {
            if (0 == (*iter))
            {
                iter = mListDelegates.erase(iter);
            }
            else
            {
                (*iter)->invoke();
                ++iter;
            }
        }
    }

private:
    ListDelegate mListDelegates; // 链表容器
};
#endif
// 统一创建委托对象的回调函数
// 通过不同的函数重载,可以返回对应的委托
inline IDelegate *newDelegate(void (*_func)())
{
    return new CStaticDelegate(_func);
}

template <class T>
inline IDelegate *newDelegate(T *_object, void (T::*_method)())
{
    return new CMethodDelegate<T>(_object, _method);
}

// 测试
class TestClass
{
public:
    static void print_static()
    {
        cout << "静态函数" << endl;
    }
    void print()
    {
        cout << "成员函数" << endl;
    }
};
void print()
{
    cout << "全局函数" << endl;
}
int main()
{
    CMultiDelegate delegate;
    TestClass c;

    delegate += newDelegate(print);
    delegate += newDelegate(&TestClass::print_static); // 静态函数
    delegate += newDelegate(&c, &TestClass::print);    // 普通成员函数,对象调用

    delegate();
    return 0;
}

C语言回调函数

C语言的回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

#include <stdio.h>

void printWelcome(int len)
{
    printf("欢迎%d\n", len);
}

void printGoodbye(int len)
{
    printf("送客%d\n", len);
}

void callback(int times, void (*print)(int))
{
    int i;
    for (i = 0; i < times; ++i)
    {
        print(i); // 调用第二个参数对应的函数
    }
}
void main(void)
{
    callback(1, printWelcome);//欢迎0
    callback(2, printGoodbye);//送客0  送客1
    printWelcome(2);//欢迎2
}

typedef的作用:一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明,结合typedef可以简化回调函数类型声明。

#include <stdio.h>
typedef void (*print)();

void callback(print fun)
{
    fun();
}

void printWelcome()
{
    printf("欢迎\n");
}

void main(void)
{
    callback(printWelcome);//欢迎
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#委托、C++委托实现、C回调函数 的相关文章

  • 在 Vulkan 中,图形队列系列与当前队列系列分离是否有益?

    据我所知 队列系列可能支持呈现到屏幕但不支持图形 假设我有一个同时支持图形和呈现的队列系列 以及另一个仅支持呈现的队列系列 我应该为两个进程使用第一个队列系列 还是应该将第一个队列系列委托给图形 将后者委托给呈现 或者这两种方法之间没有明显
  • 如何从字符串中提取子字符串直到遇到第二个空格?

    我有一个像这样的字符串 o1 1232 5467 1232 5467 1232 5467 1232 5467 1232 5467 1232 5467 如何仅提取 o1 1232 5467 要提取的字符数并不总是相同 因此 我只想提取直到遇到
  • C# SmtpClient编程中如何设置带有中文的附件文件名?

    我的代码如下 ContentType ct new ContentType ct MediaType MediaTypeNames Application Octet ct Name 这是一个很长的中文文件名希望能用它在附件名中 Doc A
  • 关于逻辑/算法的想法以及如何防止线程写入 Sql Server 中的竞争

    我有以下逻辑 public void InQueueTable DataTable Table int incomingRows Table Rows Count if incomingRows gt RowsThreshold async
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • 无法继承形状

    为什么我不能使用继承 a 的类Shapes class http msdn microsoft com en us library ms604615 28v vs 90 29 我需要延长Rectangle具有一些方法的类 但我想以与使用相同
  • 混合模型优先和代码优先

    我们使用模型优先方法创建了一个 Web 应用程序 一名新开发人员进入该项目 并使用代码优先方法 使用数据库文件 创建了一个新的自定义模型 这 这是代码第一个数据库上下文 namespace WVITDB DAL public class D
  • 如何向 Mono.ZeroConf 注册服务?

    我正在尝试测试 ZeroConf 示例http www mono project com Mono Zeroconf http www mono project com Mono Zeroconf 我正在运行 OpenSuse 11 和 M
  • OpenGL:如何检查用户是否支持glGenBuffers()?

    我检查了文档 它说 OpenGL 版本必须至少为 1 5 才能制作glGenBuffers 工作 用户使用的是1 5版本但是函数调用会导致崩溃 这是文档中的错误 还是用户的驱动程序问题 我正在用这个glGenBuffers 对于VBO 我如
  • Linux 上的 RTLD_LOCAL 和dynamic_cast

    我们有一个由应用程序中的一些共享库构成的插件 我们需要在应用程序运行时更新它 出于性能原因 我们在卸载旧插件之前加载并开始使用新插件 并且只有当所有线程都使用旧插件完成后 我们才卸载它 由于新插件和旧插件的库具有相同的符号 我们dlopen
  • 保证复制省略是否适用于函数参数?

    如果我理解正确的话 从 C 17 开始 这段代码现在要求不进行任何复制 Foo myfunc void return Foo auto foo myfunc no copy 函数参数也是如此吗 下面的代码中的副本会被优化掉吗 Foo myf
  • LinkLabel 无下划线 - Compact Framework

    我正在使用 Microsoft Compact Framework 开发 Windows CE 应用程序 我必须使用 LinkLabel 它必须是白色且没有下划线 因此 在设计器中 我将字体颜色修改为白色 并在字体对话框中取消选中 下划线
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView?

    我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表 MousePos 是一个自定义类 它保存鼠标 X Y 坐标 类型为 Point 和该位置的运行计数 我有一个线程 System Timers Timer
  • 如何从 Boost.PropertyTree 复制子树

    我有一些boost property tree ptree 我需要树来删除一些具有特定标签名称的元素 例如 xml 表示源ptree如下
  • 构建 C# MVC 5 站点时项目之间的处理器架构不匹配

    我收到的错误如下 2017 年 4 月 20 日构建 13 23 38 C Windows Microsoft NET Framework v4 0 30319 Microsoft Common targets 1605 5 警告 MSB3
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配
  • 如何为有时异步的操作创建和实现接口

    假设我有数百个类 它们使用 计算 方法实现公共接口 一些类将执行异步 例如读取文件 而实现相同接口的其他类将执行同步代码 例如将两个数字相加 为了维护和性能 对此进行编码的好方法是什么 到目前为止我读到的帖子总是建议将异步 等待方法冒泡给调
  • 如何在 ASP.NET Core 中注入泛型的依赖关系

    我有以下存储库类 public class TestRepository Repository

随机推荐

  • 宁波到西塘可以坐火车去吗?

    宁波 嘉善 西塘宁波 嘉善火车 N406 N407 空调快速宁波15 10出发嘉善19 27到达4小时17分270公里硬座 42元硬卧 93元然后从嘉善打车到西塘 9公里 15元左右 坐快客3元也可以乘坐T794 空调特快宁波 10 41出
  • python 实现自动批量下载腾讯在线excel

    python 批量下载腾讯在线文档 如需要源代码供参考 可以留言邮箱 看到的话就发一下 pthon自动批量下载腾讯在线文档 对于大量实时更新维护的在线文档 可以随时轻松自动化批量下载在线文档 无需耗费人工下载 腾讯在线文档标签有时候会随版本
  • BP神经网络算法基本原理,BP神经网络算法流程图

    伤寒 副伤寒流行预测模型 BP神经网络 的建立 由于目前研究的各种数学模型或多或少存在使用条件的局限性 或使用方法的复杂性等问题 预测效果均不十分理想 距离实际应用仍有较大差距 NNT是Matlab中较为重要的一个工具箱 在实际应用中 BP
  • 爬虫中有关验证码的问题处理

    在爬虫中 经常要处理登陆的相关事宜 有时候登陆界面会需要提交验证码 如何处理验证码 解决办法 若是自己编写模块 需要涉及深度学习 这就是另一块大的内容了 在这里简单调用已经封装好的模块来实现获取验证码 本文以超级鹰为例 网址 http ww
  • PADS 原理图如何自动编号

    PADS原理图如何自动编号 PADS 原理图工具 PowerLogic exe 不支持元件位号重名 原生不提供自动编号功能 虽然 PowerPCB exe 可以支持元件位号自动编号功能再同步到原理图 但是其局限性太大没啥实际意义 另外 PA
  • YUV格式学习:YUYV、YVYU、UYVY、VYUY格式转换成RGB

    YUYV YVYU UYVY VYUY格式 它们都是YUV422的打包格式 即在内存中 Y U V都是挨着排序的 它们的名称就表示了Y U V的顺序 像YUYV 就是Y U Y V Y U Y V 在做转换时 就显得很容易 简单了 因为极其
  • 【卷积核设计】10、Scaling Up Your Kernels to 31x31

    文章目录 一 背景 二 方法 三 RepKLNet a Large Kernel Architecture 3 1 结构 3 2 尽可能的让卷积核变大 3 3 图像分类 3 4 语义分割 3 5 目标检测 四 分析 五 限制 六 结论 论文
  • 【笔试强训选择题】Day43.习题(错题)解析

    作者简介 大家好 我是未央 博客首页 未央 303 系列专栏 笔试强训选择题 每日一句 人的一生 可以有所作为的时机只有一次 那就是现在 文章目录 前言 一 Day43习题 错题 解析 总结 前言 一 Day43习题 错题 解析 1 解析
  • Elasticsearch笔记4 基础入门

    执行分布式检索 一个查询操作 在ES分布式环境中分为两步 查询与合并 查询阶段 ES集群向所有分片传递查询语句 分片接收到请求后 执行搜索并建立一个长度为top n的优先队列 存储结果 top n 的大小取决于分页参数 top n from
  • 链表c语言stl,C++STL之List容器

    1 再谈链表 List链表的概念再度出现了 作为线性表的一员 C 的STL提供了快速进行构建的方法 为此 在前文的基础上通过STL进行直接使用 这对于程序设计中快速构建原型是相当有必要的 这里的STL链表是单链表的形式 2 头文件 头文件
  • Vue3+Vite项目配置Eslint+Prettier+Husky+Lint-Staged+Commitlint

    Eslint 配置 ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具 ESLint 能够帮你轻松写出高质量的 JavaScript 代码 1 建议 vscode 安装 Eslint 的插件 这个插件
  • 用JAVASCRIPT从弹出的窗口中获取值

    设三个页面 Father aspx SubOpen aspx SubModalDialog aspx 在Father aspx中
  • Excel构建决策分析模型

    特点 探讨使用 Excel 构建决策模型的价值和重要性 以及对 Excel 复杂性的非常详细和深入的解释 使用 Excel 的图形功能来有效地呈现定量数据 比率和间隔 来通知和影响目标对象 利用 Excel 的内置数据可视化和操作功能准备数
  • kubernetes session保持等设置

    session保持 如何在service内部实现session保持呢 当然是在service的yaml里进行设置啦 在service的yaml的sepc里加入以下代码 sessionAffinity ClientIP sessionAffi
  • matplotlib怎么在一张图上画多条曲线?

    问题 多个plot画不到一张图上 解决方法 多个plot用一个plt show 即可 一次plt show 就会有一次输出 如何让函数画在同一张画布上 for i in range 1 15 3 train score test score
  • bh1750c语言程序,BH1750FVI数字光线强度传感器 51单片机源程序

    BH1750FVI IIC测试程序 使用单片机STC89C51 晶振 11 0592M 显示 LCD1602 编译环境 Keil uVision2 参考宏晶网站24c04通信程序 时间 2011年4月20日 include include
  • vue3表格导出excel

    下载依赖 npm install xlsx 引入依赖 import as XLSX from xlsx 使用
  • Python内置类型转换函数

    chr i chr 函数返回ASCII码对应的字符串 gt gt gt print chr 65 A gt gt gt print chr 66 B gt gt gt print chr 65 chr 66 AB complex real
  • stm32f103zet6移植标准库的sdio驱动

    sdio移植 st官网给的标准库有给一个用于st出的评估板的sdio外设实现 但一是文件结构有点复杂 二是相比于国内正点原子和野火的板子也有点不同 因此还是需要移植下才能使用 当然也可以直接使用正点原子或野火提供的实例 但为了熟悉下sdio
  • C#委托、C++委托实现、C回调函数

    C 委托 C 中的委托 Delegate 定义了方法的类型 使得可以将方法当作另一个方法的参数来进行传递 这种将方法动态地赋给参数的做法 可以避免在程序中大量使用if else switch 语句 同时使得程序具有更好的可扩展性 委托特别用