马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

C#委托(delegate、Action、Func、predicate)和事件

[复制链接]
查看3044 | 回复0 | 2019-9-17 11:34:51 | 显示全部楼层 |阅读模式

一、媒介

刚开始工作的时间,觉得委托和事件有些神秘,而当你理解他们之后,也觉得似乎没有想象中的那么难。在项目中运用委托和事件,你会发现他非常棒,这篇博文算是自己对委托和事件的一次梳理和总结。

二、委托

C#中的委托,相称于C++中的指针函数,但委托是面向对象的,是安全的,是一个特殊的类,当然他也是引用类型,委托传递的是对方法的引用。

2.1、delegate

声明委托就必须使用关键字“delegate”,委托是先声明,后实例化。至少0个参数,至多32个参数

格式如下所示:

  1. private delegate string GetAsString();
复制代码

委托是一个类,以是他的实例化跟类的实例化一样,只是他总是担当一个将委托方法作为参数的构造函数。调用委托方法就有两种方式,如下所示:

  1. int i = 10;
  2. var method = new GetAsString(i.ToString);
  3. //调用方法一
  4. Console.WriteLine($"method方法{method()}");
  5. //调用方法二
  6. Console.WriteLine($"method.Invoke方法{method.Invoke()}");
复制代码

运行效果:

113452t34qnk4z4gme3wqq.png

2.2、Action

Action是无返回值的泛型委托,可以担当0个至16个传入参数

Action 表示无参,无返回值的委托

Action 表示有传入参数int,string无返回值的委托

前面我们【Log4Net 日记记录的实现】中,就使用了Action。如:

  1. public static void Debug(string message, Action RegistedProperties)
  2. {
  3. RegistedProperties();
  4. log.Debug(message);
  5. }
复制代码

调用方式为:

  1. PFTLog.Debug("测试扩展字段", () => {
  2. LogicalThreadContext.Properties["LogType"] = "扩展字段内容";
  3. });
复制代码

在运行中,直接运行Action中的内容即可。

2.3、Func

Func是有返回值的泛型委托,可以担当0个至16个传入参数

Func 表示无参,返回值为int的委托

Func 表示传入参数为object, string 返回值为int的委托

  1. public static decimal GetTotal(Func<int, int, decimal> func, int a, int b)
  2. {
  3. return func(a, b);
  4. }
复制代码

调用方式

  1. var total = GetTotal((a, b) => { return (decimal)a + b; }, 1, 2);
  2. Console.WriteLine($"效果为{total}");
复制代码

运行效果

113452rktccpleg3hpltcl.png

2.4、predicate

predicate 是返回bool型的泛型委托,只能担当一个传入参数

predicate 表示传入参数为int 返回bool的委托

界说一个方法:

  1. public static bool FindPoints(int a)
  2. {
  3. return a >= 60;
  4. }
复制代码

界说Predicate委托

  1. Predicate<int> predicate = FindPoints;
复制代码

调用

  1. var points = new int[] {
  2. 10,
  3. 50,
  4. 60,
  5. 80,
  6. 100 };
  7. var result = Array.FindAll(points, predicate);
  8. Console.WriteLine($"效果为{string.Join(";", result)}");
复制代码

运行效果

113453k06d0j7ee6e645xz.png

2.5、多播委托

前面的只包含了一个方法的调用,委托可以包含多个方法,这种委托就叫做多播委托。多播委托利用“+=”和“-+”两种运算符举行添加和删除委托。

先界说两个方法

  1. public static void MultiplyByTwo(double v)
  2. {
  3. double result = v * 2;
  4. Console.WriteLine($"传值:{v};MultiplyByTwo效果为{result}");
  5. }
  6. public static void Square(double v)
  7. {
  8. double result = v * v;
  9. Console.WriteLine($"传值:{v};Square效果为{result}");
  10. }
复制代码

然后调用

  1. Action<double> operations = MultiplyByTwo;
  2. operations(1);
  3. operations += Square;
  4. operations(2);
复制代码

运行效果:

113453hwgwaip27z40ga2k.png

三、事件

事件是基于委托,为委托提供一种发布/订阅机制,声明事件须要使用event关键字。

发布者(Publisher):一个事件的发行者,也称作是发送者(sender),实在就是个对象,这个对象会自行维护自己的状态信息,当自己状态信息变动时,便触发一个事件,并关照说有的事件订阅者;

订阅者(Subscriber):对事件感兴趣的对象,也称为Receiver,可以注册感兴趣的事件,在事件发行者触发一个事件后,会主动实行这段代码

是不是看到sender,就有种很认识的感觉!!!先不忙着急,我们先看下事件的声明和使用

有这样一个应用场景,假如系统有异常,须要实时的关照管理员。那么须要在我们的日记记录内里添加关照管理员的功能,但是题目来了,该怎么关照管理员呢?至少现在无法知道。以是我们就须要在使用到事件。

添加代码如下,假如不知道日记功能的可以参考【Log4Net 日记记录的实现】

  1. //声明一个关照的委托
  2. public delegate void NoticeEventHander(string message);
  3. //在委托的机制下我们建立以个关照事件
  4. public static event NoticeEventHander OnNotice;
复制代码

调用方式

  1. public static void Debug(string message, Action RegistedProperties)
  2. {
  3. RegistedProperties();
  4. log.Debug(message);
  5. //实行关照
  6. OnNotice?.Invoke($"系统异常,请实时处理处罚,异常信息:{message}");
  7. }
复制代码

在引用场景的代码,先界说一个关照管理员的方法(这里我们直接Console.WriteLine出来)

  1. public static void Notice(string message)
  2. {
  3. Console.WriteLine($"关照内容为{message}");
  4. }
复制代码

先注册,然后触发异常消息

  1. //注册方式一
  2. PFTLog.OnNotice += Notice;
  3. //注册方式二
  4. //PFTLog.OnNotice += new PFTLog.NoticeEventHander(Notice);
  5. PFTLog.Debug("测试扩展字段", () => {
  6. LogicalThreadContext.Properties["LogType"] = "扩展字段内容";
  7. });
复制代码

运行效果

113453qw8c11isovh1sh8g.png

这内里我只须要界说好发布者,你可以以任何方式订阅,是不是很非常简单。

弄明白了上面的事件,我们在来说说.Net经常出现的object sender和EventArgs e

.Net Framework的编码规范:

一、委托类型的名称都应该以EventHandler竣事

二、委托的原型界说:有一个void返回值,并担当两个输入参数:一个Object 类型,一个 EventArgs类型(或继续自EventArgs)

三、事件的定名为 委托去掉 EventHandler之后剩余的部门

四、继续自EventArgs的类型应该以EventArgs末端

现在我们以一个新书发布的自界说事件为例

创建对应的类文件:

113453a1uccrv8u8d1dzbv.png

事件者发布代码:

  1. public class BookInfoEventArgs : EventArgs
  2. {
  3. public BookInfoEventArgs(string bookName)
  4. {
  5. BookName = bookName;
  6. }
  7. public string BookName { get; set; }
  8. }
复制代码
  1. public class BookDealer
  2. {
  3. //泛型委托,界说了两个参数,一个是object sender,第二个是泛型 TEventArgs 的e
  4. //简化了如下的界说
  5. //public delegate void NewBookInfoEventHandler(object sender, BookInfoEventArgs e);
  6. //public event NewBookInfoEventHandler NewBookInfo;
  7. public event EventHandler<BookInfoEventArgs> NewBookInfo;
  8. public void NewBook(string bookName)
  9. {
  10. RaiseNewBookInfo(bookName);
  11. }
  12. public void RaiseNewBookInfo(string bookName)
  13. {
  14. NewBookInfo?.Invoke(this, new BookInfoEventArgs(bookName));
  15. }
  16. }
复制代码

事件订阅者

  1. public class Consumer
  2. {
  3. public Consumer(string name)
  4. {
  5. Name = name;
  6. }
  7. public string Name { get; set; }
  8. public void NewBookHere(object sender, BookInfoEventArgs e)
  9. {
  10. Console.WriteLine($"用户:{Name},收到书名为:{ e.BookName}");
  11. }
  12. }
复制代码

事件订阅和取消订阅

  1. var dealer = new BookDealer();
  2. var consumer1 = new Consumer("用户A");
  3. dealer.NewBookInfo += consumer1.NewBookHere;
  4. dealer.NewBook("book112");
  5. var consumer2 = new Consumer("用户B");
  6. dealer.NewBookInfo += consumer2.NewBookHere;
  7. dealer.NewBook("book_abc");
  8. dealer.NewBookInfo -= consumer1.NewBookHere;
  9. dealer.NewBook("book_all");
复制代码

运行效果

113454u55qto4o5o0of54z.png

颠末这个例子,我们可以知道Object sender参数代表的是事件发布者自己,而EventArgs e 也就是监督对象了。深入理解之后,是不是觉得也没有想象中的那么难了。

四、总结

这里我们讲了委托和事件,在.Net开辟中使用委托和事件,可以淘汰依靠性和层的耦合,开辟出具有更高的重用性的组件。







来源:https://www.cnblogs.com/snailblog/archive/2019/09/14/11520438.html
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则