【目录】

1 委托

2 事件-概念的引出

3 事件-关于异常

4 事件-关于异步

5 委托-Func与Action

1 委托

在.NET中定义“委托”需要用到delegate关键字,它是存有对某个方法的引用的一种引用类型变量,类似于 C 或 C++ 中函数的指针。“委托”主要有两大作用:

(1)将方法当作参数传递

(2)方法的一种多态(类似于一个方法模板,可以匹配很多个方法)

下面,给出一个展现了上述两大作用的委托代码示例:

        //定义一个委托
public delegate int MyDelegate(int x, int y); //与委托匹配的一个方法
public static int Add(int a, int b)
{
return a + b;
} //与委托匹配的另一个方法
public static int Reduce(int a, int b)
{
return a - b;
} //示例:将委托/方法当参数传递
public static int Test(MyDelegate MD)
{
return MD(10, 20);
} static void Main(string[] args)
{
int a, b, x, y; MyDelegate md; //将委托指向Add这个方法,并进行相关操作
md = Add;
a = md(1, 2);
b = Test(md); //再将委托指向Reduce这个方法,并进行相关操作
md = Reduce;
x = md(7, 2);
y = Test(md); Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
Console.ReadLine();
}

执行以上程序,输出结果如下:

1+2=3,10+20=30,7-2=5,10-20=-10

委托也可以使用+=/-=来实现“发布/订阅”模式,示例代码如下:

        //定义一个委托
public delegate void MyDelegate1(int x); public static void Method1(int a)
{
Console.WriteLine($"a={a}");
} public static void Method2(int b)
{
Console.WriteLine($"b={b}");
} static void Main(string[] args)
{
MyDelegate1 md = null;
md += Method1;
md += Method2;
md(35); Console.ReadLine();
}

以上程序输出如下:

a=35

b=35

但是委托有一个弊端,它可以使用“=”将所有已经订阅的取消,只保留=后的这一个订阅。

为了解决这个弊端,事件event应运而生。

2 事件-概念的引出

事件event是一种特殊的委托,它只能+=,-=,不能直接用=。

event在定义类中(发布者)是可以直接=的,但是在其他类中(订阅者)就只能+= -=了,也就是说发布者发布一个事件后,订阅者针对他只能进行自身的订阅和取消。

下面是定义一个事件的代码:

        //定义一个委托
public delegate void MyDelegate1(int x);
//定义一个事件
public event MyDelegate1 emd;

经过长久的经验积累后,人们发现,绝大多数事件的定义,是用public delegate void XXX(object sender, EventArgs e);这样一个委托原型进行定义的,是一件重复性的工作,于是,EventHandler应运而生。它的出现就是为了避免这种重复性工作,并建议尽量使用该类型作为事件的原型。

//@sender: 引发事件的对象
//@e: 传递的参数
public delegate void EventHandler(object sender, EventArgs e); //使用
public event EventHandler emd;

下面给出一个使用事件的具体示例:

        public class Demo
{
public event EventHandler emd;
public void RaiseEvent()
{
emd(this, EventArgs.Empty);
}
} static void Main(string[] args)
{
var instance = new Demo();
instance.emd += (sender, arg) =>
{
Console.WriteLine("执行事件1!");
}; instance.emd += (sender, arg) =>
{
Console.WriteLine("执行事件2!");
}; instance.RaiseEvent(); Console.ReadLine();
}

这里我们先定义一个Demo类,其内部有个事件是emd,我们给他开放了一个接口RaiseEvent,如果谁敢调用它,那么,它就触发报警事件emd。

这里模拟了2个订阅者,分别处理报警事件emd。

程序执行结果如下:

执行事件1!

执行事件2!

同时,我们也可以看出:事件是按照+=的订阅先后顺序执行的。

3 事件-关于异常

现在,我们在第一个订阅者中加入异常,如下:

    instance.emd += (sender, arg) =>
{
Console.WriteLine("执行事件1!");
throw new Exception("执行事件1,错误");
};

执行后发现,第1个订阅者事件触发抛出异常后,第2个订阅者的事件没有执行。

可见,如果你想让所有订阅者都执行处理的话,那每个订阅者必须在订阅程序内自己处理好异常,不能抛出来!

4 事件-关于异步

如果事件的订阅者中有一个是“异步”处理,又会是什么情况?

下面我们把第1个订阅者改为异步处理,代码如下:

    instance.emd += async (sender, arg) =>
{
Console.WriteLine("执行事件1!");
await Task.Delay(1000);
Console.WriteLine("执行事件1!完毕");
};

执行后输出如下:
执行事件1!

执行事件2!

执行事件1!完毕

可见,异步的事件处理没有阻塞进程,很好的起到了异步方法的作用。

5 委托-Func与Action

本文最开始探讨委托,然后直接顺到了事件的相关话题上。其实,关于委托还有一个重点话题漏掉了,那就是Func与Action。

在委托delegate出现了很久以后,微软的.NET设计者们终于领悟到,其实所有的委托定义都可以归纳并简化成只用Func与Action这两个语法糖来表示。其中,Func代表有返回值的委托,Action代表无返回值的委托。有了它们两,我们以后就不再需要用关键字delegate来定义委托了。

同时,若再用lambda表达式取代被委托指向的具体方法,则整个委托的“定义+赋值”两步将大大简化(lambda表达式本来也是方法定义的一种简化形式)。

下面,把最开始委托章节中关于加减法的程序代码,用Func与lambda表达式进行简化改造,改造后的代码如下:

        //示例:将委托/方法当参数传递
public static int Test(Func<int, int, int> MD)
{
return MD(10, 20);
} static void Main(string[] args)
{
int a, b, x, y; Func<int, int, int> md; //将委托指向加法,并进行相关操作
md = (t, v) => t + v;
a = md(1, 2);
b = Test(md); //再将委托指向减法,并进行相关操作
md = (t, v) => t - v;
x = md(7, 2);
y = Test(md); Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
Console.ReadLine();
}

是不是代码大大简化了?简化了哪些内容,你可以前后对比一下...(本文完)

彻底弄懂C#中delegate、event、EventHandler、Action、Func的使用和区别的更多相关文章

  1. 彻底弄懂AngularJS中的transclusion

    点击查看AngularJS系列目录 彻底弄懂AngularJS中的transclusion AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个 ...

  2. 一文弄懂神经网络中的反向传播法——BackPropagation【转】

    本文转载自:https://www.cnblogs.com/charlotte77/p/5629865.html 一文弄懂神经网络中的反向传播法——BackPropagation   最近在看深度学习 ...

  3. 【转】彻底弄懂Java中的equals()方法以及与"=="的区别

    彻底弄懂Java中的equals()方法以及与"=="的区别 一.问题描述:今天在用Java实现需求的时候,发现equals()和“==”的功能傻傻分不清,导致结果产生巨大的偏差. ...

  4. 【TensorFlow】一文弄懂CNN中的padding参数

    在深度学习的图像识别领域中,我们经常使用卷积神经网络CNN来对图像进行特征提取,当我们使用TensorFlow搭建自己的CNN时,一般会使用TensorFlow中的卷积函数和池化函数来对图像进行卷积和 ...

  5. JavaScript中的this详解(彻底弄懂js中的this用法)!

    要想学好js,那么其中那些特别令人混淆迷惑的知识点,就一定要弄清楚.this关键字就是其中让初学者比较迷惑的知识点之一,不过灵活运用this可以提升代码的性能和复用性,那么今天我就和大家一起来了解th ...

  6. 一文弄懂神经网络中的反向传播法——BackPropagation

    最近在看深度学习的东西,一开始看的吴恩达的UFLDL教程,有中文版就直接看了,后来发现有些地方总是不是很明确,又去看英文版,然后又找了些资料看,才发现,中文版的译者在翻译的时候会对省略的公式推导过程进 ...

  7. 彻底弄懂css中单位px和em,rem的区别

    国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢? PX特点 -1. IE无法调整那些使用px作为单位的字体大小: -2. 国外的大部分网站能够调 ...

  8. 彻底弄懂css中单位px和em,rem的区别 转的自己看

    国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢? PX特点 1. IE无法调整那些使用px作为单位的字体大小: 2. 国外的大部分网站能够调整的 ...

  9. 彻底弄懂css中单位px和em的区别(转)

    国内的设计师大都喜欢用px,而国外的网站大都喜欢用em,那么两者有什么区别,又各自有什么优劣呢? 1. IE无法调整那些使用px作为单位的字体大小: 2. 国外的大部分网站能够调整的原因在于其使用了e ...

  10. 弄懂css中单位px和em,rem的区别

              国内的设计师大都喜欢用px,而国外的网站大都喜欢用em和rem,那么三者有什么区别,又各自有什么优劣呢?         PX特点 1. IE无法调整那些使用px作为单位的字体大小 ...

随机推荐

  1. JAVA基础Day1-注释/标识符和关键字/数据类型/类型转换/变量、常量、作用域

    目录 一.注释 二.标识符和关键字 标识符命名需要注意: 三.数据类型 基本数据类型: 拓展: 定义时需要注意: 四.类型转换 字节 五.变量.常量.作用域 变量 变量命名规范 变量作用域 常量 一. ...

  2. 【python】第一模块 步骤四 第二课、实现飞机大战(未完待续)

    第二课.实现飞机大战 一.项目介绍 项目实战:飞机大战 课程目标 掌握面向对象分析和开发的思想 能对项目进行拆分,进行模块化开发 了解项目开发的基本流程 理解并运用python的包.模块相关知识 理解 ...

  3. UE C++教程之接口 UINTERFACE

    我是谁不重要,重要的是,我能做什么. 近期笔者在进行UE的开发时,实现多武器的换弹与开火需要用到接口.而笔者以前是做Unity开发的,遂没有使用过UE C++的UINTERFACE,而这个接口在使用过 ...

  4. 修改allure图标和标题

    allure的logo更换步骤 1.找到allure安装目录,进入目录如:D:\Program Files\allure-2.17.3\plugins\custom-logo-plugin\stati ...

  5. windows IIS http 自动转https

    1.安装url重写组件 https://www.iis.net/downloads/microsoft/url-rewrite#additionalDownloads 2.刷新IIS 3.添加规则

  6. javaweb本地启动很快,服务器上面启动特别慢

    在JVM环境中解决 打开$JAVA_PATH/jre/lib/security/java.security这个文件,找到下面的内容: securerandom.source=file:/dev/ura ...

  7. JAVA面经-基础篇-线程

    1.创建线程有哪几种方式?   创建线程有3种方式,分别是继承Thread类.实现Runnable类.实现Callable类.   继承Thread类的步骤:     1. 定义Thread类的子类, ...

  8. 【Unity】Lua热重载

    写在前面 本文讨论的"Lua热重载"是基于他人现成工具和相关博文上展开的,所以这里并不会重复实现一遍工具,主要记录我的理解过程. Lua热重载 探索 偶然在知乎上翻到一篇文章&qu ...

  9. 使用netty 实现本地代理程序

    本地代理程序1:将远程的服务设置为本地端口访问我的台式PC安装了vm,因为都是机器私有IP,但我的另外的PC电脑也需要访问方便测试,需要要把VM的端口设置在台式本机对外,这样我台式的端口对外在局域网都 ...

  10. C++ 几款IDE和编程平台的选择分析

    最近闲来无事,就研究了一下几个编程平台和IDE.首先,我必须强调一下,这些方案研究并不一定适用于商业公司内部编程平台选择,而是给个人学习或者闲暇之余把玩用的.主要从以下几个指标考量:使用体验.跨平台. ...