C#中委托、事件和回调的使用及说明
作者:云无极 发布时间:2023-08-13 01:00:24
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
事件是对委托的封装。如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用到新的委托对象,也就删除了当前要调用的方法列表;
定义一个事件有两步,首先定义一个委托,它包括了这件事的“协议”和委托方法(由谁去做);其次,用event关键字和相关委托声明这个事件。事件像是一个接口,封装了委托所定的“协议”。由于委托已经定义了协议,剩下的就是按这个协议去办事,至于怎么做它并不关心。调用者无法访问委托对象。
回调函数就是把一个方法的传给另外一个方法去执行。回调函数只是一个功能片段,由用户按照回调函数的调用约定来实现的一个函数。可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。而设计上有一个抽离出可变部分代码的原则,这种用法无疑可以用到那种场合了。从上可知,事件和回调都是对委托的一种用法。事件是把委托封装起来,而回调函数则是由委托绑定不同的函数来实现不同的功能。
委托的使用案例
定义委托和方法
//委托定义(要与方法中参数一致)
public delegate int DelegateTest(int n1, int n2);
class Math
{
//方法定义(委托要执行的方法,本案例把方案写到class类中)
public int Multiply(int n1, int n2)
{
return n1 * n2;
}
public int AddTest(int n1, int n2)
{
return n1 + n2;
}
}
委托的使用
private void button1_Click(object sender, EventArgs e)
{
Math objMath = new Math();
//创建委托对象
DelegateTest delegateDemo1;
//将方法与委托对象关联起来 (委托:将方法当作另一个方法的参数来进行传递)
//delegateDemo1 = new CallDelegate(objMath.Multiply);
delegateDemo1 = objMath.Multiply; //与上面方法相同
//delegateDemo1 += objMath.AddTest; //给委托对象再绑定一个方法,若该条代码执行,显示结果为17
//将委托实例化
int result = delegateDemo1(5, 12);
richTextBox1.AppendText(result.ToString() + "\r");
//****委托的另外一写法,通过Action或Func,如果有返回值用Func,否则用Action *****//
//Func<int, int, int> func1 = new Func<int, int, int>(objMath.Multiply);
Func<int, int, int> func1 = objMath.Multiply;
richTextBox1.AppendText(func1.Invoke(6, 13) + "\r");
}
执行的结果
利用Action或Func简化代码
private void button4_Click(object sender, EventArgs e)
{
Math objMath = new Math();
//委托的另外一写法,通过Action或Func,如果有返回值用Func,否则用Action
//Func<int, int, int> func1 = new Func<int, int, int>(objMath.Multiply); //同下
Func<int, int, int> func1 = objMath.Multiply;
richTextBox1.AppendText(func1.Invoke(6, 13) + "\r");
}
事件的使用案例
class ClassA
{
public string ClassAinfo = "A 默认!";
public void DispInfo()
{
ClassAinfo = "A 收到!";
}
}
class ClassB
{
public string ClassAinfo = "B 默认!";
public void DispInfo()
{
ClassAinfo = "B 收到!";
}
}
class DelegatEventTest
{
//定义委托
public delegate void MyDelegateEventHandler();
//定义事件
public event MyDelegateEventHandler NotifyEveryOne;
//调用事件
public void Notify()
{
if (NotifyEveryOne != null)
{
NotifyEveryOne();
}
}
}
事件的使用
private void button3_Click(object sender, EventArgs e)
{
//创建classA和classB的对象
ClassA objA = new ClassA();
ClassB objB = new ClassB();
//创建委托的对象
DelegatEventTest event1 = new DelegatEventTest();
//订阅事件(类似于 方法与委托事件的关联)
event1.NotifyEveryOne += new DelegatEventTest.MyDelegateEventHandler(objA.DispInfo);
//event1.NotifyEveryOne += new DelegatEventTest.MyDelegateEventHandler(objB.DispInfo);
event1.Notify();
richTextBox1.AppendText(objA.ClassAinfo + "\r");
richTextBox1.AppendText(objB.ClassAinfo + "\r");
}
带参数的事件案例
参考网上的案例,场景:首领boyK要搞一场鸿门宴,吩咐部下boyA和boyB各自带队埋伏在屏风两侧,约定以杯为令:若左手举杯,则boyA带队杀出;若右手举杯,则boyB带队杀出;若直接摔杯,则boyA和boyB同时杀出。boyA和boyB袭击的具体方法,首领boyK并不关心。
boyK的定义
public class BoyK
{
//定义委托
public delegate void RaiseEventHandler(string hand);
public delegate void FallEventHandler();
//定义事件
public event RaiseEventHandler RaiseEvent;
public event FallEventHandler FallEvent;
//调用事件(例:举手事件)
public void Raise(string hand)
{
if (RaiseEvent!=null)
{
RaiseEvent(hand);
}
}
//调用事件(例:摔杯事件)
public void Fall()
{
if (FallEvent!=null)
{
FallEvent();
}
}
}
boyA的定义
class BoyA
{
public string str = "A待命";
BoyK boyk;
public BoyA(BoyK k)
{
boyk = k;
k.RaiseEvent += new BoyK.RaiseEventHandler(k_RaiseEvent); //订阅举杯事件
k.FallEvent += new BoyK.FallEventHandler(k_FallEvent); //订阅摔杯事件
}
public void Attack()
{
str = "A开始**";
}
//boyK举杯的动作
void k_RaiseEvent(string hand)
{
if (hand.Equals("左"))
{
Attack();
}
}
void k_FallEvent()
{
Attack();
}
}
boyB的定义
class BoyB
{
public string str = "B待命";
BoyK boyk;
public BoyB(BoyK k)
{
boyk = k;
k.RaiseEvent += new BoyK.RaiseEventHandler(k_RaiseEvent); //订阅举杯事件
k.FallEvent += new BoyK.FallEventHandler(k_FallEvent); //订阅摔杯事件
}
public void Attack()
{
str = "B开始**";
}
void k_RaiseEvent(string hand)
{
if (hand.Equals("右"))
{
Attack();
}
}
void k_FallEvent()
{
Attack();
}
}
事件的使用
private void button2_Click(object sender, EventArgs e)
{
BoyK boyK = new BoyK();
BoyA boyA = new BoyA(boyK);
BoyB boyB = new BoyB(boyK);
//boyK.Raise("左");
boyK.Raise("右");
//boyK.Fall();
richTextBox1.AppendText(boyA.str+"\r");
richTextBox1.AppendText(boyB.str + "\r");
}
运行效果
回调函数的使用案例
实际开发中,下面这个类会封装起来,只提供函数接口。相当于系统底层
//实际开发中,下面这个类会封装起来,只提供函数接口。相当于系统底层
class CalculateClass
{
public delegate int SomeCalculateWay(int num1, int num2);
//将传入参数在系统底层进行某种处理,具体计算方法由开发者开发,函数仅提供执行计算方法后的返回值
//下面的代码中相当于调用了一个回调函数
public int Calculate(int num1, int num2, SomeCalculateWay call)
{
return call(num1, num2);
}
}
开发层处理,开发人员编写具体的计算方法
//开发层处理,开发人员编写具体的计算方法
class FunctionClass
{
public int GetSum(int a, int b)
{
return a + b;
}
public int GetMulti(int a, int b)
{
return a * b;
}
}
用户层,执行输入等操作
private void button4_Click(object sender, EventArgs e)
{
CalculateClass cc = new CalculateClass();
FunctionClass fc = new FunctionClass();
int result1 = cc.Calculate(2, 3, fc.GetSum);
int result2 = cc.Calculate(2, 3, fc.GetMulti);
richTextBox1.AppendText(result1 + "\r");
richTextBox1.AppendText(result2 + "\r");
}
说明:上述代码中的FunctionClass中的GetSum()和GetMulti()两个函数称为回调函数。
可以看到整个程序中并没有哪个地方通过类似GetSum(1,2)这种形式调用了该函数,只有将其当作另一个函数的参数来进行调用。
如cc.PrintAndCalculate(2, 3, fc.GetSum)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/zhigedali/article/details/122928563


猜你喜欢
- 前言:各位小伙伴们,大家好,一日不见,如隔一日,今天我给大家分享一下大家在学习java过程当中遇到的一个问题,也是一道面试题,java中,O
- 我们就可以结合今天的Annotation Processing Tool(APT)来自定义注解处理器。注解处理器简单解释就是收集我们标记的注
- java中map与实体类的相互转换1. 在 pom.xml 中引入依赖包<dependency> <grou
- Feign Client 超时时间配置不生效解决方案Feign Client 的 connectTimeout 和 readTimeout
- 前言Mybatis作为一个应用广泛的优秀的ORM框架,已经成了JavaWeb世界近乎标配的部分,这个框架具有强大的灵活性,在四大组件(Exe
- 一、获取接口请求的数据可以在Interceptor的afterCompletion中实现但是要重写RequestWrapper代码记录如下:
- ? 通配符类型<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类;<? super T&
- ButterKnife的最新版本是8.4.0。首先,需要导入ButterKnife的jar包。在AndroidStudio中,File-&g
- 前言本文将实现一个MyBatis的Springboot的Starter包,引用这个Starter包后,仅需要提供少量配置信息,就能够完成My
- Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "hello";,另一种就是使用new
- 提到Excel的导入导出,大家肯定都知道alibaba开源的EasyExcel,该项目的github地址为:https://github.c
- java中的复杂查询sql语句实现:这样的查询栏,在页面中很常见,这就是复杂查询的条件,我们可以填写一个条件或多个条件,也可以一个都不填写,
- 什么是volatile关键字volatile是Java中用于修饰变量的关键字,其可以保证该变量的可见性以及顺序性,但是无法保证原子性。更准确
- 介绍技术之前有用eureka 现在用nacos工作流和gateway接口数据流向数据表新建项目新建cloud-删除src-新建modleEu
- 目录1)在程序集中添加资源2)在程序集中查找资源这一篇单独拿出来分析这个程序集资源,为的就是不想让大家把程序集资源和exe程序强关联,因为程
- 导入依赖(pom.xml) <!--整合Shiro安全框架--> <dependency>
- 本文介绍的仿IOS对话框的实现,先来看一下效果图具体代码如下:public class AlertDialog { private Cont
- Spring main方法调用Dao层和Service层的方法在web环境中,一般serviceImpl中的dao之类的数据库连接都由容器启
- C# 关于Invoke首先说下,invoke和begininvoke的使用有两种情况:control中的invoke、begininvoke
- 释一:属性的访问器包含与获取(读取或计算)或设置(写)属性有关的可执行语句。访问器声明可以包含 get 访问器或 set 访问器,或者两者均