C# Delegate 委托及事件
1、委托Delegate实质
由一个修饰符+ delegate,跟方法的定义比较类似,也需要声明参数和返回值。声明一个委托,就是声明一种方法签名(参数+返回值),只要是和声明委托方法签名相同的方法,都可以被委托实例托管。
理解:具有相同方法签名的方法(method),他们的调用都可以通过相同方法签名的委托(class)去实现
比如现在有很多排队的需求:排队买奶茶、排队拿号买房。。。,这些需求可让一个排队服务机构去完成,这个机构只需要定义一项委托服务:指派一个人去排队获取xxx
public delegate object WaitGetAccesss(Person person)
还比如快递服务是不是也是类似的场景,快递的本质是把东西从地点A搬运到地点B,同样可以定义public delegate void WaitGetAccesss(object obj,area A,area B);只需要告知要运输的货物、起始地点A、目的地点B即可(这里只针对运输的部分,不包含派送的部分)
几乎所有的快递运输都可以通过这个方式去完成,无论是数码产品、食品这些
自己能做的事情,让这个排队服务机构完成,这不就是现实世界里委托的含义吗?
public delegate void MyFirstDelegate()
委托的实质是一个类,通过反编译得到MyFirstDelegate如下,这个类继承MulticastDelegate,类的内部包含ctor的构造函数,Invoke、BeginInvoke、EndInvoke方法
.class nested public auto ansi sealed MyFirstDelegate
extends [System.Runtime]System.MulticastDelegate
{
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
object 'object',
native int 'method'
) runtime managed
{
} // end of method MyFirstDelegate::.ctor .method public hidebysig newslot virtual
instance int32 Invoke (
int32 x,
int32 y
) runtime managed
{
} // end of method MyFirstDelegate::Invoke .method public hidebysig newslot virtual
instance class [System.Runtime]System.IAsyncResult BeginInvoke (
int32 x,
int32 y,
class [System.Runtime]System.AsyncCallback callback,
object 'object'
) runtime managed
{
} // end of method MyFirstDelegate::BeginInvoke .method public hidebysig newslot virtual
instance int32 EndInvoke (
class [System.Runtime]System.IAsyncResult result
) runtime managed
{
} // end of method MyFirstDelegate::EndInvoke } // end of class MyFirstDelegate
2、委托的定义及执行
定义
委托的参数方法的签名要和委托的方法签名一致(即要有相同的参数及返回值)
public delegate int NumberHandleDelegate(int a,int b);
public static int AddNum(int a, int b)
{
return a+b;
}
//标准写法
NumberHandleDelegate numberHandleDelegate1 = new NumberHandleDelegate(AddNum);
//语法糖,编译器使用的便捷功能,帮忙省略了new NumberHandleDelegate的编写
NumberHandleDelegate numberHandleDelegate2 = AddNum;
//lambda表达式(匿名方法)
NumberHandleDelegate numberHandleDelegate3 = (int a, int b) => a + b;
执行
{
Student s = new Student();
int num = s.numberHandleDelegate1.Invoke(3, 5);
Console.WriteLine($"num:{num}");
//结果打印:num:8
}
3、实际的应用
3.1应用一
public enum PersonType
{
Student = 1, Teacher = 2
}
现在想针对不同的人有不同的打招呼的方式,有以下两种方案
//方案1:定义枚举,通过枚举判断,分别给出不同的问候方式;
public void SayHi(PersonType type)
{
switch (type)
{
case PersonType.Student:
Console.WriteLine("同学好");
break;
case PersonType.Teacher:
Console.WriteLine("老师好");
break;
default:
throw new Exception("没有找到具体类型");
}
}
//方案2:根据不同的类型的人,分别定义不同的问候方式
public void SayStudentHi()
{
Console.WriteLine("同学好");
} public void SayTeacherHi()
{
Console.WriteLine("老师好");
}
优劣分析
//场景1:如果增加一个类型的人
//方案1:多种分支判断 ---如果增加一个类型的人------增加一个校长;---校长好!
// 问题:SayHi方法不稳定,一旦增加一个类型的人;SayHi方法就需要修改;方法的所有逻辑都选哟重新测试. 一个方法中有多种业务逻辑---业务逻辑耦合
//方案2:每个方法都式独立的:
// 问题:---如果增加一个类型的人---增加一个方法;功能独立,对其他的功能内部不影响; //场景2:如果需要增加一个功能的业务逻辑呢?--打招呼后再握手
//方案1:增加功能的业务逻辑很方便;只需要在方法内加上 Console.WriteLine("握手");
//方案2:增加公共逻辑--每个方法都需要增加--增加的代码都是一样的,----大量的重复代码;
利用委托能覆盖以上两种场景,完美解决以上问题
//定义一个打招呼的委托
public delegate void SayHiDelegate();
//定义一个公共方法,参数为打招呼的委托
public void SayHiPerfect(SayHiDelegate sayHiDelegate)
{
sayHiDelegate.Invoke();
Console.WriteLine("握手");
}
调用:
//方案三:
student.SayHiPerfect(student.SayStudentHi);
student.SayHiPerfect(student.SayTeacherHi);
3.2应用二——利用属性和委托实现对方法的包装
public class Teacher
{
[PassExam]
[Register]
[PreparaLessons]
public void Teach()
{
Console.WriteLine($"老师教学生上课,核心内容!!!");
} public void show()
{
Teacher teacher = new Teacher();
Type type = typeof(Teacher);
TeachProjectDelegate projectDelegate = new TeachProjectDelegate(teacher.Teach);
foreach (var methodInfo in type.GetMethods())
{
if (methodInfo.IsDefined(typeof(AbstractAttribute), true))
{
foreach (AbstractAttribute attribute in methodInfo.GetCustomAttributes(typeof(AbstractAttribute)).Reverse())
{
projectDelegate = attribute.AddPreWork(projectDelegate);
}
}
}
projectDelegate.Invoke(); }
}
定义的特性AbstractAttribute,及其其他子类PassExamAttribute、PreparaLessonsAttribute、RegisterAttribute
public abstract class AbstractAttribute:Attribute
{
public abstract TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate);
}
public class PassExamAttribute:AbstractAttribute
{
public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
{
TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
{
Console.WriteLine("老师要通过课程认证考试");
teachProjectDelegate.Invoke();
Console.WriteLine("课程认证考试成功通过"); }); return dDelegate;
}
}
public class PreparaLessonsAttribute : AbstractAttribute
{
public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
{
TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
{
Console.WriteLine("老师首先得备课》》》");
teachProjectDelegate.Invoke();
Console.WriteLine("老师备课》》》成功"); }); return dDelegate;
}
}
public class RegisterAttribute:AbstractAttribute
{
public override TeachProjectDelegate AddPreWork(TeachProjectDelegate teachProjectDelegate)
{
TeachProjectDelegate dDelegate = new TeachProjectDelegate(() =>
{
Console.WriteLine("注册成为老师");
teachProjectDelegate.Invoke();
Console.WriteLine("注册老师成功"); }); return dDelegate;
}
}
执行及其结果:
{
Teacher teacher = new Teacher();
teacher.show();
//老师要通过课程认证考试
//注册成为老师
//老师首先得备课》》》
//老师教学生上课,核心内容!!!
//老师备课》》》成功
//注册老师成功
//课程认证考试成功通过
}
可以看到经过特性+委托的多层嵌套封装——程序的执行环节自动装配。如果有十个环节,只需要定义10个特性
一个特性就可以增加一个处理环节,这也是ASP.NET core的管道处理模型的思想
四、内置的委托类型——Action、Func、predicate
4.1 Action
Action——无返回值的泛型委托。
Action 表示无参,无返回值的委托
Action<int,string> 表示有传入参数int,string无返回值的委托
Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托
Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托
public void Test<T>(Action<T> action,T p)
{
action(p);
}
4.2. Func
Func是有返回值的泛型委托
Func<int> 表示无参,返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托
Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
public int Test<T1,T2>(Func<T1,T2,int> func,T1 a,T2 b)
{
return func(a, b);
}
4.3 Predicate
predicate 是返回bool型的泛型委托
predicate<int> 表示传入参数为int 返回bool的委托
Predicate有且只有一个参数,返回值固定为bool
例:public delegate bool Predicate<T> (T obj)
五、多播委托
可以理解成,多件事一起放到委托,一起执行
C#-多播委托 - 闻风听雨 - 博客园 (cnblogs.com)
+=是按照注册的顺序去执行的(顺序)、-=是按照逆序的方式去匹配,匹配到一个取消注册随即结束不会继续向下匹配,如果没有匹配到也不会报错
//+=是按照添加的顺序去执行的(顺序)、-=是按照逆序的方式去匹配,匹配到一个即停止,如果没有匹配到也不会报错
Console.WriteLine("\n");
Console.WriteLine("**********多播委托示例*******");
NoreturnDelegate dDelegate = () => Console.WriteLine("22333");
dDelegate += WriteSomething2;
dDelegate += new Teacher().Teach;
dDelegate += WriteSomething2;
dDelegate.Invoke();
// Console.Read(); Console.WriteLine("\n");
Console.WriteLine("**********多播取消打印*******");
dDelegate -= WriteSomething2;
dDelegate.Invoke();
//**********多播委托示例 * ******
//22333
//这里有一些东西
//老师教学生上课, 核心内容!!!
//这里有一些东西 //********** 多播取消打印*******
//22333
//这里有一些东西
//老师教学生上课, 核心内容!!!
public static void WriteSomething2()
{
Console.WriteLine("这里有一些东西");
}
public delegate int NoParameterNoReturnDelegate();
六、观察者模式
C#设计模式-观察者模式 - 晓镜水月 - 博客园 (cnblogs.com)
public class Student
{
public void Package()
{
Console.WriteLine("学生收拾行李");
}
public void DoneWork()
{
Console.WriteLine("学生准备好作业");
}
}
public class School
{
public void AnnounceTermBegins()
{
Console.WriteLine("我宣布明天开学了。。。。。");
}
/// <summary>
/// 我们想按照顺序执行一套程序
/// 学校——宣布开学
/// 妈妈——给孩子买文具、买衣服
/// 学生——做好作业、打包行李
/// 如图这样将这一套宣布开学后的流程封装到学校这个类显然不合理:
/// 1、首先违反单一职责原则:妈妈给孩子准备衣服文具、学生做好专业、收拾自己的东西和学校不相干
/// 2、开放封闭原则:后续如果宣布开学了,新增了家长收拾打扮、送孩子上学这些时间,又需要在修改学校开学的这个方法、这显然不合理
/// </summary>
//public void TermBegins()
//{
// AnnounceTermBegins();
// Mother mother = new Mother();
// Student student = new Student();
// mother.BuyShoes();
// mother.BuyTool();
// student.DoneWork();
// student.Package();
//} ///委托实现观察者模式
public Action TermBeginsAction; public void TermBegins()
{
AnnounceTermBegins();
TermBeginsAction?.Invoke();//如果TermBeginsAction不为空,就执行开学的方法
} ///事件实现观察者模式
public event Action termBeginEvent ;
public void TermBeginsEvent()
{
termBeginEvent?.Invoke();//如果TermBeginsAction不为空,就执行开学的方法
} }
internal class Mother
{
public void BuyTool()
{
Console.WriteLine("家长给孩子买文具");
}
public void BuyShoes()
{
Console.WriteLine("家长给孩子买衣服。。。。。");
}
}
//七、观察者模式(行为)帅锅行为,将自身行为引起得其他行为统一封装,引起的得其他行为的增加或删减的逻辑丢到外部,无论引起了什么其他行为,都不改变自身的封装
//
{
Console.WriteLine("\n");
Console.WriteLine("**********委托实现观察者模式*******");
//缺陷:外部能直接执行委托,例如sc.TermBeginsAction.Invoke();设定的逻辑有不被执行的风险(可能在先宣告开学的时候就把后续的行为做了)
School sc = new School();
Mother mother = new Mother();
Student student = new Student();
sc.TermBeginsAction += mother.BuyShoes;
sc.TermBeginsAction += mother.BuyTool;
sc.TermBeginsAction += student.DoneWork;
sc.TermBeginsAction += student.Package; sc.AnnounceTermBegins();
sc.TermBegins();//等价与sc.TermBeginsAction.Invoke();
//sc.TermBeginsAction.Invoke(); //**********委托实现观察者模式 * ******
//我宣布明天开学了。。。。。
//家长给孩子买衣服。。。。。
//家长给孩子买文具
//学生准备好作业
//学生收拾行李 Console.WriteLine("\n");
Console.WriteLine("**********事件实现观察者模式*******");
School sc1 = new School();
Mother mother1 = new Mother();
Student student1 = new Student();
sc1.termBeginEvent += mother1.BuyShoes;
sc1.termBeginEvent += mother1.BuyTool;
sc1.termBeginEvent += student1.DoneWork;
sc1.termBeginEvent += student1.Package;
sc1.TermBeginsEvent();
//sc1.termBeginEvent.Invoke();无法执行,因为事件只能在类的内部调用执行,更安全
//只能在类的内部执行,类的子类都不能执行 //**********事件实现观察者模式 * ******
// 家长给孩子买衣服。。。。。
//家长给孩子买文具
// 学生准备好作业
//学生收拾行李 }
C# Delegate 委托及事件的更多相关文章
- [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)
原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...
- Delegate(委托与事件)
Delegate可以当它是一个占位符,比如你在写代码的时候并不知道你将要处理的是什么.你只需要知道你将要引入的参数类型和输出类型是什么并定义它即可.这就是书本上所传达的方法签名必须相同的意思. 系统自 ...
- 第一章、C#委托和事件(Delegate、Event、EventHandler、EventArgs)
第一章.C#委托和事件(Delegate.Event.EventHandler.EventArgs) 分类: 学习笔记-C#网络编程2012-12-08 14:10 7417人阅读 评论(3) 收藏 ...
- 委托和事件[delegate and event]_C#
委托和事件: 1. 委托:一个能够表示方法的数据类型:它将方法作为对象封装起来,允许在运行时间接地绑定一个方法调用. 2. 声明委托数据类型: public delegate bool Greate ...
- 转载: jQuery事件委托( bind() \ live() \ delegate()) [委托 和 绑定的故事]
转载:http://blog.csdn.net/zc2087/article/details/7287429 随着DOM结构的复杂化和Ajax等动态脚本技术的运用,事件委托自然浮出了水面.jQuery ...
- C# Note2:委托(delegate) & Lambda表达式 & 事件(event)
前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...
- 委托与事件--delegate&&event
委托 访问修饰符 delegate 返回值 委托名(参数); public delegate void NoReturnNoPara(); public void NoReturnNoParaMeth ...
- .NET面试题系列[7] - 委托与事件
委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- [转载]C#深入分析委托与事件
原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.c ...
随机推荐
- 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
在人工智能飞速发展的今天,大语言模型的应用越来越广泛.DeepSeek 作为近期爆火的一款大语言模型,受到了众多开发者的青睐. 今天这篇内容,就来聊聊,如何在本地自己的电脑上部署DeepSeek. 1 ...
- 面试官:说说你项目中JWT的执行流程?
JWT 在目前的项目开发中使用到的频率是非常高的,因此它也是面试常问的一类问题,所以今天我们就来看看"项目中 JWT 的执行流程?"这个问题. 1.什么是 JWT? JWT(JSO ...
- linux命令行连接wifi
linux命令行连接wifi 1.安装nmcli sudo apt-get install nmcli 2.查看网络设备 sudo nmcli dev 3.开启wifi sudo nmcli r wi ...
- 安装mysql报错5.7.13-Table 'mysql.user' doesn't existFor more information
临时写的一个小系统客户要求用mysql,所以下载一个来研究下.解压后开始配置my.ini 配置my.ini [mysql]# 设置mysql客户端默认字符集default-character-set= ...
- 附039.Kubernetes_v1.32.2高可用部署架构二
部署组件 该 Kubernetes 部署过程中,对于部署环节,涉及多个组件,主要有 kubeadm .kubelet .kubectl. kubeadm介绍 Kubeadm 为构建 Kubernete ...
- 清华大学推出的5册免费的 DeepSeek 学习使用指南!
前言 在当今这个信息洪流.技术飞速迭代的时代,DeepSeek的横空出世极大地降低了普通人利用人工智能技术的门槛.然而,尽管机遇就在眼前,仍有不少朋友面对DeepSeek感到无从下手,不知如何利用它来 ...
- 浅谈Tox之一
本文分享自天翼云开发者社区<浅谈Tox之一>,作者:Moonriver What is tox? tox是通用的virtualenv管理和测试命令行工具,可用于: 使用不同的Python版 ...
- 【编程思想】C# delegate 委托的本质:方法对象的应用
一.前言 翻回之前写的博客,前期写的结构确实差很多, 这次细看了<委托那些事(一).(二)>,忍不住重新写一下,之前把简单的事情复杂化了. 为什么现在思维不一样了,有一点我认为是见识的计算 ...
- JMeter BeanShell 获取 HTTP Request 中的 Name
场景:添加 JMeter log 输出,想输入自定义请求的名称 // 获取 response body prev.getResponseDataAsString(); // 获取 HTTP Reque ...
- Prometheus Go client library 详解
介绍 Prometheus 支持 4 种 指标类型,分别是 Counter.Gauge.Histogram 和 Summary. Counter 指标类型,指标值是只能递增,不能递减的数值.需要注意的 ...