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 委托及事件的更多相关文章

  1. [转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

    原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委 ...

  2. Delegate(委托与事件)

    Delegate可以当它是一个占位符,比如你在写代码的时候并不知道你将要处理的是什么.你只需要知道你将要引入的参数类型和输出类型是什么并定义它即可.这就是书本上所传达的方法签名必须相同的意思. 系统自 ...

  3. 第一章、C#委托和事件(Delegate、Event、EventHandler、EventArgs)

    第一章.C#委托和事件(Delegate.Event.EventHandler.EventArgs) 分类: 学习笔记-C#网络编程2012-12-08 14:10 7417人阅读 评论(3) 收藏  ...

  4. 委托和事件[delegate and event]_C#

    委托和事件: 1. 委托:一个能够表示方法的数据类型:它将方法作为对象封装起来,允许在运行时间接地绑定一个方法调用. 2. 声明委托数据类型: public delegate  bool Greate ...

  5. 转载: jQuery事件委托( bind() \ live() \ delegate()) [委托 和 绑定的故事]

    转载:http://blog.csdn.net/zc2087/article/details/7287429 随着DOM结构的复杂化和Ajax等动态脚本技术的运用,事件委托自然浮出了水面.jQuery ...

  6. C# Note2:委托(delegate) & Lambda表达式 & 事件(event)

    前言 本文主要讲述委托和Lambda表达式的基础知识,以及如何通过Lambda表达式实现委托调用,并阐述.NET如何将委托用作实现事件的方式. 参考:C#高级编程 1.什么是委托(delegate)? ...

  7. 委托与事件--delegate&&event

    委托 访问修饰符 delegate 返回值 委托名(参数); public delegate void NoReturnNoPara(); public void NoReturnNoParaMeth ...

  8. .NET面试题系列[7] - 委托与事件

    委托和事件 委托在C#中具有无比重要的地位. C#中的委托可以说俯拾即是,从LINQ中的lambda表达式到(包括但不限于)winform,wpf中的各种事件都有着委托的身影.C#中如果没有了事件,那 ...

  9. .NET基础拾遗(4)委托、事件、反射与特性

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

  10. [转载]C#深入分析委托与事件

    原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.c ...

随机推荐

  1. 深入掌握 SQL 深度应用:复杂查询的艺术与技巧

    title: 深入掌握 SQL 深度应用:复杂查询的艺术与技巧 date: 2025/2/10 updated: 2025/2/10 author: cmdragon excerpt: SQL(结构化 ...

  2. .Net 配置绑定 IOptions

    准备   首先准备下appsettins.json以及目标类   appsettins.json "StudentSettings": { "Id": 1023 ...

  3. Vue3+NestJS实现后台权限管理系统上线啦!(附源码及教程)

    最近这段时间工作不忙,想着提升一下自己的技术,沉淀沉淀.于是做了一个开源的后台权限管理系统.因为我本身是一个前端开发,所以前端和服务端都是用的 JS 语言来开发的,前端用的框架是 vue3,后端则用的 ...

  4. [JOISC 2023 Day3] Tourism 题解

    大家好,我喜欢珂朵莉树,所以我用珂朵莉树 \(AC\) 了本题. 实际上,我们比较容易发现,这题实际上就是求 \([l,r]\) 中的所有点作为关键点时,虚树所压缩的所有点(实际上就是显现出来的点+在 ...

  5. [TJOI2015] 弦论 题解

    所有子串,一眼 \(\text{SAM}\). 从根开始一直往下走,走到任何一个点都代表一个子串.维护 \(sm\) 表示每个子串有几个(\(t=0\) 就当一个),可以用树形 \(dp\) 跳后缀链 ...

  6. 【博客搭建】Latex数学书写笔记

    [博客搭建]Latex 数学书写笔记 Latex 是一种文档书写语言,支持大量的特殊字符,包括书写数学公式,并且很多 Markdown 环境都支持该语言. 布局实现 靠左:使用内联数学块$...$. ...

  7. autMan奥特曼机器人-青龙运行结果推送到autMan

    一.使用到的autMan云插件为"青龙推送autMan"或"JD未来活动定时运行" 二选一即可,两都不可同时安装,有冲突 青龙推送autMan:这个插件仅用于将 ...

  8. Edge、谷歌浏览器默认下载器开启多线程下载

    浏览器默认下载器开启多线程下载 Chrome 浏览器,地址栏输入并回车: chrome://flags/#enable-parallel-downloading Edge 新版浏览器,地址栏输入并回车 ...

  9. 通过Kube-rbac-proxy保护 Kubernetes 工作负载中的应用容器

    1.概述 kube-rbac-proxy 是 Kubernetes 生态中一个专注于"基于角色的访问控制(RBAC)"的轻量级代理组件,通常以 Sidecar 容器的形式部署在 P ...

  10. DBeaver连接mysql时Public Key Retrieval is not allowed错误

    前言 DBeaver 连接 mysql 时,报错:Public Key Retrieval is not allowed 解决 在新建连接的时候,驱动属性里设置 allowPublicKeyRetri ...