当前位置: 移动技术网 > IT编程>开发语言>c# > C#委托与事件初探

C#委托与事件初探

2019年07月18日  | 移动技术网IT编程  | 我要评论
委托给了c#操作函数的灵活性,我们可使用委托像操作变量一样来操作函数,其实这个功能并不是c#的首创,早在c++时代就有函数指针这一说法,而在我看来委托就是c#的函数指针,首

委托给了c#操作函数的灵活性,我们可使用委托像操作变量一样来操作函数,其实这个功能并不是c#的首创,早在c++时代就有函数指针这一说法,而在我看来委托就是c#的函数指针,首先先简要的介绍一下委托的基本知识:

委托的定义

委托的声明原型是

delegate <函数返回类型> <委托名> (<函数参数>)

例子:public delegate void checkdelegate(int number);//定义了一个委托checkdelegate,它可以注册返回void类型且有一个int作为参数的函数

这样就定义了一个委托,但是委托在.net内相当于声明了一个类(在后面的代码中会讲到确实如此),类如果不实例化为对象,很多功能是没有办法使用的,委托也是如此.

委托的实例化

委托实例化的原型是

<委托类型> <实例化名>=new <委托类型>(<注册函数>)

例子:checkdelegate _checkdelegate=new checkdelegate(checkmod);//用函数checkmod实例化上面的checkdelegate 委托为_checkdelegate

在.net 2.0开始可以直接用匹配的函数实例化委托:

<委托类型> <实例化名>=<注册函数>

例子:checkdelegate _checkdelegate=checkmod;//用函数checkmod实例化上面的checkdelegate 委托为_checkdelegate
现在我们就可以像使用函数一样来使用委托了,在上面的例子中现在执行_checkdelegate()就等同于执行checkmod(),最关键的是现在函数checkmod相当于放在了变量当中,它可以传递给其它的checkdelegate引用对象,而且可以作为函数参数传递到其他函数内,也可以作为函数的返回类型

事件是委托的一种特殊形式,当发生有意义的事情时,事件处理对象通知过程。

一.c语言中的函数指针

  想要理解什么是委托,就要先理解函数指针的概念。所谓函数指针,就是指向函数的指针(等于没说-.-)。比如我定义了两个函数square和cube分别用于计算一个数的平方和立方,我再定义函数指针calcu,然后我让calcu指向square,那么调用calcu时就相当于调用了square函数(注意,此处函数指针接受的参数类型及个数要与函数一致)。很好理解吧?不多说,上代码。

#include <stdio.h>
void square(int x) { printf("square of %d is %d\n",x,x*x); }
void cube(int x) { printf("cube of %d is %d\n",x,x*x*x); }
int main()
{
void (*calcu)(int x);
calcu=square;
calcu();
return ;
}

二.c#中委托的实质

  委托又名委托类型,为什么c#弄出这个东西?因为c#是一门比较安全的语言,不允许操作指针,于是我们不能定义函数指针。但想要达到相同的效果,于是定义了委托类型。所谓委托类型,其本质就是c中的指针类型。于是代码变成了这样:

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
namespace delegate
{
class program
{
static void square(int x) { console.writeline("square of {} is {}", x, x * x); }
static void cube(int x) { console.writeline("cube of {} is {}", x, x * x * x); }
delegate void math(int x); //定义委托类型
static void main(string[] args)
{
math calcu;
calcu += square;
calcu();
console.readkey();
}
}
} 

  可以看出,定义委托类型math实际上就相当于定义了void*类型。而委托类型实例化得到的calcu实际上就是函数指针。(说句题外话:定义函数(方法)时要加上static是因为调用函数时并未实例化,只有静态方法能够直接通过类调用)。

三.委托的使用方法

  我们在上述代码19行后面加上一行代码 calcu+=cube; 运行会发现,square和cube均被调用。可以看出,符号 += 表示绑定方法到委托变量,同理符号 -= 表示取消绑定。可以理解为calcu是void **类型,即它指向了一个数组,数组中的每一项都是函数指针类型,每次调用calcu时,遍历此数组,即依次调用每个绑定的方法。

四.封装与事件的引入

  下面我们要用面向对象的思想将上述代码进行封装,使其变清晰。

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
namespace delegate
{
public delegate void math(int x);
public class calcu
{
public math calcu;
}
class program
{
static void square(int x) { console.writeline("square of {} is {}", x, x * x); }
static void cube(int x) { console.writeline("cube of {} is {}", x, x * x * x); }
static void main(string[] args)
{
calcu c = new calcu();
c.calcu += square;
c.calcu += cube;
c.calcu();
console.readkey();
}
}
}

由于委托变量是public的,封装的程度很低,在外部可以任意修改。为了改进这个问题,c#引入了事件。

  所谓事件,实际上还是委托的实例化,只是其内部多了一些定义,多了一些限制。其一,事件实际上声明了一个private类型的委托变量,因此在类外无法直接调用。

  于是我们将上述代码的第12行改成这样:

public event math calcu;

  运行之后25行报错了,因为calcu是private的,不能直接调用。但23,24行并没有报错。那么问题来了,为什么我们可以用+=来给calcu绑定方法呢?

  因为其二,事件还帮我们干了一件事情,就是定义了绑定方法和取消绑定方法的函数,它们是public的,并且将运算符+=,-=重载,和这两个函数对应。

  好了,现在我们要写一个接口函数来完成计算:

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
namespace delegate
{
public delegate void math(int x);
public class calcu
{
public event math calcu;
public void calculate(int x)
{
calcu(x);
}
}
class program
{
static void square(int x) { console.writeline("square of {} is {}", x, x * x); }
static void cube(int x) { console.writeline("cube of {} is {}", x, x * x * x); }
static void main(string[] args)
{
calcu c = new calcu();
c.calcu += square;
c.calcu += cube;
c.calculate();
console.readkey();
}
}
}

  至此,基本概念已经清晰。

  想来,使用事件会让人不得不将对象封装起来,这应该就是面向对象思想的体现吧。

以上内容是针对c#委托与事件初探的相关知识,希望对大家有所帮助。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

  • c# 面试必备线程基础知识点

    c# 面试必备线程基础知识点

    线程的知识太多,知识点有深有浅,往深的研究会涉及操作系统、cpu、内存,往浅了说就是一些语法。没有一定的知识积累,很难把线程的知识写得全面,当然我也没有这个能力... [阅读全文]
  • C#使用System.Net邮件发送功能踩过的坑

    C#使用System.Net邮件发送功能踩过的坑

    1.eazyemail邮件发送类库net 类库自带了邮件发送功能。笔者对该类库,从使用的角度进行了二次封装,nuget上可搜索eazyemail,注入容器时通过... [阅读全文]
  • C#基于Modbus三种CRC16校验方法的性能对比

    C#基于Modbus三种CRC16校验方法的性能对比

    1.背景介绍主要应用场景在物联网中,底端设备注册报文的上报,需要对报文的有效载荷(data)进行crc16的复验,验证与设备端的crc校验是否相等,如果相等,报... [阅读全文]
  • 深入谈谈C#9新特性的实际运用

    前言你一定会好奇:“老周,你去哪开飞机了?这么久没写博客了。”老周:“我买不起飞机,开了个铁矿,挖了一年半的石头。谁知铁矿垮了,压死了几条蜈蚣,什么也没挖着。”... [阅读全文]
  • C# 泛型集合的自定义类型排序的实现

    C# 泛型集合的自定义类型排序的实现

    一、泛型集合list<t>排序经sort方法之后,采用了升序的方式进行排列的。二、对自定义类型进行排序定义一个普通类:接下来,将定义的person实... [阅读全文]
  • C#开发中常用的加密解密方法汇总

    相信很多人在开发过程中经常会遇到需要对一些重要的信息进行加密处理,今天给大家分享我个人总结的一些加密算法:常见的加密方式分为可逆和不可逆两种方式可逆:rsa,a... [阅读全文]
  • C# 如何添加错误日志信息

    系统日志系统日志包含了由windows系统组件记录的事件。例如,在启动期间装入驱动程序或其他系统组件失败被记录到系统日志。要查看系统日志: 打开命令提示符。 ... [阅读全文]
  • 关于C#委托三种调用的分享使用

    关于C#委托三种调用的分享使用

    一、同步调用1、同步调用会按照代码顺序来执行2、同步调用会阻塞线程,如果是要调用一项繁重的工作(如大量io操作),可能会让程序停顿很长时间,造成糟糕的用户体验,... [阅读全文]
  • 用c# 自动更新程序

    作者:冰封一夏出处:hzhcontrols官网:首先看获取和更新的接口更新程序program.cs更新程序界面定义服务端接口,你可以用任意接口都行,我这里用we... [阅读全文]
  • c# 生成二维码的示例

    二维码是越来越流行了,很多地方都有可能是使用到。如果是静态的二维码还是比较好处理的,通过在线工具就可以直接生成一张二维码图片,比如:草料二维码。但有的时候是需要... [阅读全文]
验证码:
移动技术网