首页 > 其他分享 >delegate、event、EventHandler、Action、Func的使用和区别

delegate、event、EventHandler、Action、Func的使用和区别

时间:2023-04-07 13:34:07浏览次数:133  
标签:EventHandler md Console 委托 int delegate Func public 事件

delegate、event、EventHandler、Action、Func的使用和区别

【目录】

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(); }

 

标签:EventHandler,md,Console,委托,int,delegate,Func,public,事件
From: https://www.cnblogs.com/yakniu/p/17295854.html

相关文章

  • C# javascript中调用自定义函数function
    Default.aspx1<script>2//自定义函数3functionpageInit(){4letdata=[];5varsource_data=my_source_data2();//my_source_data2是一般程序Handler.ashx中,自定义的方法6varmy_data=source_data.split('###');......
  • mysql 窗口函数(Window Functions)
    MySQL窗口函数(WindowFunctions)是一种高级的SQL查询技巧,它允许在结果集的一组相关行上执行计算。窗口函数可以用于处理分组、排序、累计等复杂的聚合任务,使得查询更加简洁和高效。在MySQL8.0及更高版本中,支持窗口函数。以下是一些常用的窗口函数:ROW_NUMBER():为结果集中的......
  • 【转】Java8之Consumer、Supplier、Predicate和Function攻略
    Java8之Consumer、Supplier、Predicate和Function攻略这几个接口都在 java.util.function 包下的,分别是Consumer(消费型)、supplier(供给型)、predicate(谓词型)、function(功能性),相信有了后面的解释,你应该非常清楚这个接口的功能了。Java8函数式接口Predicate、Consumer、Function......
  • C# 委托(delegate)、泛型委托和Lambda表达式
    #什么是委托1、从数据结构来讲,委托是和类一样是一种用户自定义类型。2、委托是方法的抽象,它存储的就是一系列具有相同参数和返回类型的方法的地址。调用委托的时候,委托包含的所有方法将被执行。#委托声明、实例化和调用 1、声明委托是一种特殊的类,因此委托的声明与类的......
  • 彻底弄懂C#中delegate、event、EventHandler、Action、Func的使用和区别
    【目录】1委托2 事件-概念的引出3 事件-关于异常4 事件-关于异步5 委托-Func与Action 1委托在.NET中定义“委托”需要用到delegate关键字,它是存有对某个方法的引用的一种引用类型变量,类似于C或C++中函数的指针。“委托”主要有两大作用:(1)将方法当作参数传递(2)......
  • C++11新特性之std::function和bind绑定器
    在C++中,存在可调用对象这一个概念,可调用对象有以下几种定义:(1).是一个函数指针(2).是一个具有operator()成员函数的类对象(仿函数)(3).是一个可被转换为函数指针的类对象(4).是一个类成员(函数指针)一、可调用对象包装器----std::functionstd::function是可调用对象的包装......
  • var b = 10; (function b() {b = 20; console.log(b); })();会打印什么你了解吗?
    要知道打印什么需要具备2个知识点一、函数声明和函数表达式的区别二、匿名函数与具名函数的区别首先来说说第一点:要区分函数声明和函数表达式,只需要知道function是不是声明中的第一个词,如果是,那就是函数声明,否则就是函数表达式;函数声明和函数表达式之间最重要的区别是......
  • 【Azure 应用服务】Function App / App Service 连接 Blob 报错
    问题描述因Blob启用了防火墙功能,但是当把AppService或FunctionApp的出站IP地址都加入到Blob的白名单中,为什么访问还是403错误呢? 问题解答AzureStorage的IP网络规则不适用于同一数据中心的客户端。存储帐户部署在同一区域中的服务使用专用的AzureIP地址进行通信。因此,不......
  • 【Azure 应用服务】Function App / App Service 连接 Blob 报错
    问题描述因Blob启用了防火墙功能,但是当把AppService或FunctionApp的出站IP地址都加入到Blob的白名单中,为什么访问还是403错误呢? 问题解答AzureStorage的IP网络规则不适用于同一数据中心的客户端。存储帐户部署在同一区域中的服务使用专用的AzureIP地址进行通信。......
  • MySqL Invalid GIS data provided to function st_geometryfromtext. 解决方法
     最近需要通过geoserver显示多边形,首先需要先将数据库的数据转换为地理数据存储类型,但由于原本的信息是按text存储的,将text类型转换为geometry就需要用到ST_GeomFromText......