翼度科技»论坛 编程开发 .net 查看内容

彻底弄懂C#中delegate、event、EventHandler、Action、Func的使用和区别

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
【目录】

1 委托

2 事件-概念的引出

3 事件-关于异常

4 事件-关于异步

5 委托-Func与Action

 
1 委托

在.NET中定义“委托”需要用到delegate关键字,它是存有对某个方法的引用的一种引用类型变量,类似于 C 或 C++ 中函数的指针。“委托”主要有两大作用:
(1)将方法当作参数传递
(2)方法的一种多态(类似于一个方法模板,可以匹配很多个方法)
下面,给出一个展现了上述两大作用的委托代码示例:
  1.         //定义一个委托
  2.         public delegate int MyDelegate(int x, int y);
  3.         //与委托匹配的一个方法
  4.         public static int Add(int a, int b)
  5.         {
  6.             return a + b;
  7.         }
  8.         //与委托匹配的另一个方法
  9.         public static int Reduce(int a, int b)
  10.         {
  11.             return a - b;
  12.         }
  13.         //示例:将委托/方法当参数传递
  14.         public static int Test(MyDelegate MD)
  15.         {
  16.             return MD(10, 20);
  17.         }
  18.         static void Main(string[] args)
  19.         {
  20.             int a, b, x, y;
  21.             MyDelegate md;
  22.             //将委托指向Add这个方法,并进行相关操作
  23.             md = Add;
  24.             a = md(1, 2);
  25.             b = Test(md);
  26.             //再将委托指向Reduce这个方法,并进行相关操作
  27.             md = Reduce;
  28.             x = md(7, 2);
  29.             y = Test(md);
  30.             Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
  31.             Console.ReadLine();
  32.         }
复制代码
执行以上程序,输出结果如下:
1+2=3,10+20=30,7-2=5,10-20=-10
委托也可以使用+=/-=来实现“发布/订阅”模式,示例代码如下:
  1.         //定义一个委托
  2.         public delegate void MyDelegate1(int x);
  3.         public static void Method1(int a)
  4.         {
  5.             Console.WriteLine($"a={a}");
  6.         }
  7.         public static void Method2(int b)
  8.         {
  9.             Console.WriteLine($"b={b}");
  10.         }
  11.         static void Main(string[] args)
  12.         {
  13.             MyDelegate1 md = null;
  14.             md += Method1;
  15.             md += Method2;
  16.             md(35);
  17.             Console.ReadLine();
  18.         }
复制代码
以上程序输出如下:
a=35
b=35
但是委托有一个弊端,它可以使用“=”将所有已经订阅的取消,只保留=后的这一个订阅。
为了解决这个弊端,事件event应运而生。
 
2 事件-概念的引出

事件event是一种特殊的委托,它只能+=,-=,不能直接用=。
event在定义类中(发布者)是可以直接=的,但是在其他类中(订阅者)就只能+= -=了,也就是说发布者发布一个事件后,订阅者针对他只能进行自身的订阅和取消。
下面是定义一个事件的代码:
  1.         //定义一个委托
  2.         public delegate void MyDelegate1(int x);
  3.         //定义一个事件
  4.         public event MyDelegate1 emd;
复制代码
经过长久的经验积累后,人们发现,绝大多数事件的定义,是用public delegate void XXX(object sender, EventArgs e);这样一个委托原型进行定义的,是一件重复性的工作,于是,EventHandler应运而生。它的出现就是为了避免这种重复性工作,并建议尽量使用该类型作为事件的原型。
  1. //@sender: 引发事件的对象
  2. //@e: 传递的参数
  3. public delegate void EventHandler(object sender, EventArgs e);
  4. //使用
  5. public event EventHandler emd;
复制代码
下面给出一个使用事件的具体示例:
  1.         public class Demo
  2.         {
  3.             public event EventHandler emd;
  4.             public void RaiseEvent()
  5.             {
  6.                 emd(this, EventArgs.Empty);
  7.             }
  8.         }
  9.         static void Main(string[] args)
  10.         {
  11.             var instance = new Demo();
  12.             instance.emd += (sender, arg) =>
  13.             {
  14.                 Console.WriteLine("执行事件1!");
  15.             };
  16.             instance.emd += (sender, arg) =>
  17.             {
  18.                 Console.WriteLine("执行事件2!");
  19.             };
  20.             instance.RaiseEvent();
  21.             Console.ReadLine();
  22.         }
复制代码
这里我们先定义一个Demo类,其内部有个事件是emd,我们给他开放了一个接口RaiseEvent,如果谁敢调用它,那么,它就触发报警事件emd。
这里模拟了2个订阅者,分别处理报警事件emd。
程序执行结果如下:
执行事件1!
执行事件2!
同时,我们也可以看出:事件是按照+=的订阅先后顺序执行的。
3 事件-关于异常

现在,我们在第一个订阅者中加入异常,如下:
  1.     instance.emd += (sender, arg) =>
  2.     {
  3.         Console.WriteLine("执行事件1!");
  4.         throw new Exception("执行事件1,错误");
  5.     };
复制代码
 
执行后发现,第1个订阅者事件触发抛出异常后,第2个订阅者的事件没有执行。
可见,如果你想让所有订阅者都执行处理的话,那每个订阅者必须在订阅程序内自己处理好异常,不能抛出来!
 
4 事件-关于异步

如果事件的订阅者中有一个是“异步”处理,又会是什么情况?
下面我们把第1个订阅者改为异步处理,代码如下:
  1.     instance.emd += async (sender, arg) =>
  2.     {
  3.         Console.WriteLine("执行事件1!");
  4.         await Task.Delay(1000);
  5.         Console.WriteLine("执行事件1!完毕");
  6.     };
复制代码
执行后输出如下:
执行事件1!
执行事件2!
执行事件1!完毕
可见,异步的事件处理没有阻塞进程,很好的起到了异步方法的作用。
 
5 委托-Func与Action

本文最开始探讨委托,然后直接顺到了事件的相关话题上。其实,关于委托还有一个重点话题漏掉了,那就是Func与Action。
在委托delegate出现了很久以后,微软的.NET设计者们终于领悟到,其实所有的委托定义都可以归纳并简化成只用Func与Action这两个语法糖来表示。其中,Func代表有返回值的委托,Action代表无返回值的委托。有了它们两,我们以后就不再需要用关键字delegate来定义委托了。
同时,若再用lambda表达式取代被委托指向的具体方法,则整个委托的“定义+赋值”两步将大大简化(lambda表达式本来也是方法定义的一种简化形式)。
下面,把最开始委托章节中关于加减法的程序代码,用Func与lambda表达式进行简化改造,改造后的代码如下:
  1.         //示例:将委托/方法当参数传递
  2.         public static int Test(Func<int, int, int> MD)
  3.         {
  4.             return MD(10, 20);
  5.         }
  6.         static void Main(string[] args)
  7.         {
  8.             int a, b, x, y;
  9.             Func<int, int, int> md;
  10.             //将委托指向加法,并进行相关操作
  11.             md = (t, v) => t + v;
  12.             a = md(1, 2);
  13.             b = Test(md);
  14.             //再将委托指向减法,并进行相关操作
  15.             md = (t, v) => t - v;
  16.             x = md(7, 2);
  17.             y = Test(md);
  18.             Console.WriteLine($"1+2={a},10+20={b},7-2={x},10-20={y}");
  19.             Console.ReadLine();
  20.         }
复制代码
是不是代码大大简化了?简化了哪些内容,你可以前后对比一下...(本文完)
 

来源:https://www.cnblogs.com/digital-college/archive/2023/04/03/17282032.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具