服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#委托与事件初探

C#委托与事件初探

2021-11-12 15:08Koala''s_Dream 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函数(注意,此处函数指针接受的参数类型及个数要与函数一致)。很好理解吧?不多说,上代码。

?
1
2
3
4
5
6
7
8
9
10
#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中的指针类型。于是代码变成了这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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时,遍历此数组,即依次调用每个绑定的方法。

四.封装与事件的引入

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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的,并且将运算符+=,-=重载,和这两个函数对应。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28