C#基础--委托

C#基础–委托

C#基础–委托

简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名)

一、什么是委托,委托的本质是什么?

  1. 跟方法有点类似,有参数,返回值,访问修饰符+ delegate

    public delegate void NoReturnNoPara();
  2. 委托的本质是一个类,继承自一个特殊类 MulticastDelegate,我们自己在定义类的时候是无法去继承MulticastDelegate

  3. 在类的内部生成一个类

image-20220324193835206

二、委托的实例化,执行委托

  1. ILSply反编译–委托的本质其实是一个类

  2. 委托本质是一个类,这个类的构造函数参数—Method–方法

image-20220324195245980

  1. 委托可以通过New来实例化,要求传递一个和这个委托的参数和返回值完全匹配的方法【完全匹配】
  • 委托有几个参数,方法参数数量也要对应;参数的类型在顺序上也要一一对应
  • 委托 返回参数和 方法 返回的参数类型也需要对应
public delegate void NoReturnWithPara(int x, int y);
private static void NoReturnNoParaMehtod()
{Console.WriteLine("这是一个无参数无返回值的方法。。。");
}
  1. 委托的实例–可以直接指向一个和这个委托参数+返回值完全匹配的方法

-语法糖–编译器给我们提供额便捷功能–new WithReturnNoPara();省略掉了

WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);
//可以省略
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;
  1. 执行委托实例的Invoke方法–去执行这个委托实例化的指向的这个方法—执行方法
//调用
NoReturnWithPara noReturnWithPara = new NoReturnWithPara(NoReturnNoParaMehtod);
noReturnWithPara.Invoke(55,68);
  1. 可以执行这个实例内部的三个方法:
//如果委托定义没有参数;在Inovke也没有参数 
withReturnNoPara.Invoke();	//开启一个新的线程去执行委托
withReturnNoPara.BeginInvoke(null, null);	//回调
withReturnNoPara.EndInvoke();
  1. 多种实例化:new、 直接指向一个方法,指向一个Lambad表达式
//new
WithReturnNoPara withReturnNoPara1 = new WithReturnNoPara(WithReturnNoParaMehtod);//直接指向一个方法
WithReturnNoPara withReturnNoPara2 = WithReturnNoParaMehtod;//指向一个Lambad表达式
NoReturnWithPara noReturnWithPara3 = (x, y) => { };

三、框架内置委托 Func 和 Action

.NET Framework3.0时代就开始拥有的产物

1. Action

Action是来自于System.RunTime的一个声明好的可以带有一个或者多个参数的委托delegate,没有返回值的委托(最多支持16个入参)

private void NoreturnNopara()
{}Action action = new Action(NoreturnNopara);
action.Invoke(); 
private void DoNothingInt(int i)
{Console.WriteLine("This is DoNothing");
}Action<int> action1 = new Action<int>(DoNothingInt);
action.Invoke(); 
Action<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action2 = null;

2. Func

  • 是来自于System.RunTime的一个声明好的可以有返回值的委托delegate,可以有参数 ,也可以没有参数(最多支持16个入参)

    private int ReturnNopara()
    {return 0;
    }Func<int> func = new Func<int>(ReturnNopara);
    
  • 如果既有参数 + 返回值,那<> 中前面类型参数=输入参数,最后的类型参数=返回值

    Func<int, int> func1 = new Func<int, int>(ToInt);Func<int, string, int> func2 = new Func<int, string, int>(DoNothingIntAndStringNew);Func<int, List<string>, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int> func3 = null;
    

问题一: 我们自己是可以定义委托的,为什么系统框架要给我们提供这样的两个委托呢?

答案一:【做到委托类型的统一】既然是系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托,这样就可以把委托做到统一;我们以后在使用委托的时候,就不用自己再去定义这个委托了;

场景:

//正常
new Thread(new ThreadStart(DoNoting));
//报错
new Thread(new NoReturnNoPara(DoNoting));//为什么ThreadStart 和 NoReturnNoPara都是没有参数没有返回的委托,但是传入NoReturnNoPara会报错呢?
//因为委托本质是一个类,你传入的实例类型和定义的参数类型不一致,当然会报错啦
//系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中都去只是用这两个委托

问题二: 那之前定义好的委托呢?

答案二: 【历史包袱】我们是去不掉的,这被称之为历史包袱;

**问题三:**什么情况下可以考虑使用委托?

**答案三:**方法内部业务逻辑耦合严重;如果多个方法中出现了很多重复代码–去除重复代码,逻辑重用,考虑委托,达到逻辑解耦的目的。

  • 从上而下:代码稳定,不需要修改;执行的逻辑不在同一个方法中–逻辑解耦
  • 从下而上:去掉了重复代码,逻辑重用

四、委托的嵌套使用(俄罗斯套娃)

Asp .Net Core的核心设计,委托实现中间件的应用;

俄罗斯套娃,属于番外装饰器–AOP支持;就是把委托这个箱子一层一层的包装起来,将要执行的核心业务逻辑封装在最内部的箱子中,执行顺序是从最外层开始到最内层,在一层一层执行出来。

每一层执行的时候,我们就可以增加一些其他的业务逻辑;-- AOP的支持,装饰器

经过特性+委托 多层嵌套的封装–程序的执行环节的自动装配;如果有十个环节,那么只需要定义10个特性,分别标记就可以增加一个处理环节–ASP.NET Core的管道处理模型。

image-20220325212909007

普通类 InvokerAction.cs – Method方法

public class InvokerAction
{[BeforeWriteLogAttribute][BeforeMethodAttribute]public void Method(int i){Console.WriteLine("这里是要执行的核心业务逻辑");}
}

普通方式:

InvokerAction invokerAction = new InvokerAction();
invokerAction.Method(256);

反射:

InvokerAction invokerAction = new InvokerAction();
Type type = invokerAction.GetType();
MethodInfo methodInfo = type.GetMethod("Method");
methodInfo.Invoke(invokerAction, new object[] { 123 });

反射+委托:

public delegate void ShowDelegate();public static void Show()
{InvokerAction invokerAction = new InvokerAction();Type type = invokerAction.GetType();MethodInfo methodInfo = type.GetMethod("Method");//实例化一个委托:委托内部包含了一个行为: 行为:执行Method方法ShowDelegate showMethod = new ShowDelegate(() =>{methodInfo.Invoke(invokerAction, new object[] { 123 });}); showMethod.Invoke();
}

套娃–多层委托的嵌套:

ShowDelegate showmthod1 = new ShowDelegate(() =>{showMthod.Invoke();});ShowDelegate showmthod2 = new ShowDelegate(() =>{showmthod1.Invoke();});ShowDelegate showmthod3 = new ShowDelegate(() =>{showmthod2.Invoke();});ShowDelegate showmthod4 = new ShowDelegate(() =>{showmthod3.Invoke();});
//执行
showmthod4.Invoke();

定义一个特性:在特性中定义每个环节中要执行的动作:

抽象类–AbstractMethodAttribute.cs

public abstract class AbstractMethodAttribute : Attribute
{/// <summary>/// 在xxx 执行之前执行的点业务落/// </summary>public abstract ShowDelegate Do(ShowDelegate action);
}

BeforeWriteLogAttribute.cs

public class BeforeWriteLogAttribute : AbstractMethodAttribute
{/// <summary>/// 在xxx 执行之前执行的点业务落/// </summary>public override ShowDelegate Do(ShowDelegate action){ ShowDelegate actionResult = new ShowDelegate(() =>{{Console.WriteLine("前面写一个日志");  //这里可以随便写。。。。}action.Invoke();{Console.WriteLine("后面写一个日志");  //这里可以随便写。。。。}});return actionResult;}
}

BeforeMethodAttribute.cs

public class BeforeMethodAttribute : AbstractMethodAttribute
{/// <summary>/// 在xxx 执行之前执行的点业务落/// </summary>public override ShowDelegate Do(ShowDelegate action){ShowDelegate actionResult = new ShowDelegate(() =>{{Console.WriteLine("在xxx 执行之前执行点业务逻辑");  //这里可以随便写。。。。}action.Invoke();{Console.WriteLine("在xxx 执行之后执行点业务逻辑");  //这里可以随便写。。。。}});//actionResult.Invoke(); return actionResult;}
}

调用:

public static void Show()
{InvokerAction invokerAction = new InvokerAction();Type type = invokerAction.GetType();MethodInfo methodInfo = type.GetMethod("Method");ShowDelegate showMethod = new ShowDelegate(() =>{methodInfo.Invoke(invokerAction, new object[] { 123 });}); //如果方法上存在继承于自AbstractMethodAttribute的属性if (methodInfo.IsDefined(typeof(AbstractMethodAttribute), true)){//获取所有属性foreach (AbstractMethodAttribute attribute in methodInfo.GetCustomAttributes().Reverse()){//第一次循环时,showMethod.Invoke 交于第一个属性执行//接下来得每次执行,都是下一个属性执行上一个属性返回来得委托//最后形成一个俄罗斯套娃,值得注意的时候,这时候所有委托并没有实际执行showMethod = attribute.Do(showMethod);	}}//返回的最后一个委托开始执行,即从套娃的最外层开始执行,最终到 showMethod.Invoke -> methodInfo.Invoke;showMethod.Invoke();
}

五、多播委托

  1. 多播委托:我们声明的任何一个委托都是多播委托;任何一个委托都是继承自MulticastDelegate(多播委托),定义的所有的委托都是多播委托;

  2. 作用:可以通过+= 把多个方法添加到这个委托中去;形成一个方法的执行链;利用 action1.Invoke() 执行委托的时候,按照添加方法的顺序,依次去执行委托;

    Action action = new Action(DoNothing);
    action += DoNothingStatic;
    action += new Student().Study;
    action += Student.StudyAdvanced;
    action += DoNothing;
    action += () =>
    {Console.WriteLine("this is 拉姆达表达式。。。");
    };
    
  3. action.BeginInvoke(); //开启一个新的线程 去执行委托

    注册有多个方法的委托,不能使用BeginInvoke;如上述action, 因添加多个方法,所以不能使用 BeginInvoke直接执行,需要用到第四点的 action.GetInvocationList() 获取委托列表循环执行;

  4. 想要开启一个新的线程去执行委托呢?

    通过action.GetInvocationList() 获取到所有的委托,然后循环,每个方法执行的时候可以BeginInvoke

    foreach (Action action1 in action.GetInvocationList())
    {//action1.Invoke();action1.BeginInvoke(null,null);
    }
    
  5. 多播委托也可以通过-=移除方法

    是从后往前,逐个匹配,如果匹配不到,就不做任何操作就不做任何操作;如果匹配到,就把当前这个移除,且停止去继续往后匹配;

    action -= DoNothing;
    
  6. 在移除的方法的时候,必须是同一个实例,同一个方法才能移除;

    action -= new Student().Study;	//没有移除掉:因为不是同一个实例
    
  7. lambda表示式是不能移除的;

    虽然方法体内容是一样的,但是lambada表示在底层会生成不同的方法

    action -= () => //没有移除:  其实是因为不是同一个方法---lambda表达式--在底层会生成不同的方法
    {Console.WriteLine("this is 拉姆达表达式。。。");
    };
    

六、观察者模式

事例:猫的故事

Cat.cs

public void Miao()
{Console.WriteLine("{0} Miao", this.GetType().Name);new Dog().Wang(); //狗叫了new Mouse().Run();//老鼠跑了new Baby().Cry(); // 小孩哭了
}

缺点:

  • 职责不单一 — 一个类中包含的职责太多了,除了Miao 一声,依赖于其他的类太多;
  • 依赖太重–依赖于Dog、Mouse、…—代码不稳定;Dog、Mouse、…任何一个类的修改,都有可能会影响到这只猫;
  • 这完全是面向过程式编程–完全是翻译需求;

改造目的:

  • 职责单一: 猫的职责仅仅只是Miao一下,其他的动作,不是猫的动作;
  • 猫Miao一声后会引发一些列的动作,应该放出去,不能属于这只猫
  • 猫Miao一声后,触发一些列的动作—不能把这一些列的动作放在猫的内部

接口类 IObject.cs:

public interface IObject
{void DoAction();
}

观察者们: 【订阅者】

public class Dog : IObject
{public void DoAction(){this.Wang();}public void Wang(){Console.WriteLine("{0} Wang", this.GetType().Name);}
}
public class Mouse : IObject
{public void DoAction(){this.Run();}public void Run(){Console.WriteLine("{0} Run", this.GetType().Name);}
}
public class Baby : IObject
{public void DoAction(){this.Cry();}public void Cry(){Console.WriteLine("{0} Cry", this.GetType().Name);}
}

发布者1:Cat.cs 【委托】

  • 通过定义委托属性
  • 给委托+=方法–Miao一声之后,要触发的动作
  • 特点:把要执行的动作不放在猫的内部从外面去指定
  • 多播委托+=来完成了一系列动作的转移,把猫Miao一声后的动作转移到上端
  • 去掉了猫对于其他的依赖—提高了猫的稳定性
  • 通过委托实现了观察者模式–观察者模式—把不属于我的动作转移出去—甩锅
public  Action MiaoAction = null; 
/// <summary>
/// 这个方法仅仅只是Miao一声
/// 引发的动作---可以放到多播委托中去
/// </summary>
public void MiaoDelegate()
{Console.WriteLine("{0} MiaoDelegate", this.GetType().Name);MiaoAction?.Invoke();//?. 如果不为null,就执行后面的动作; 若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoAction += new Dog().Wang; //狗叫了
cat.MiaoAction += new Mouse().Run;//老鼠跑了
cat.MiaoAction += new Mother().Wispher;
cat.MiaoAction += new Baby().Cry; // 小孩哭了
cat.MiaoDelegate();	//执行1cat.MiaoAction();	//和执行1一样,也可以执行

发布者2:Cat.cs 【接口 + 列表 + 循环执行】

public List<IObject> observerlist = new List<IObject>();	//IObject 含有 DoAction方法public void MiaoObsever()
{Console.WriteLine("{0} MiaoObsever", this.GetType().Name);if (observerlist.Count>0){foreach (var item in observerlist){item.DoAction();}}
}
cat.observerlist.Add(new Dog()); //狗叫了
cat.observerlist.Add(new Mouse());//老鼠跑了
cat.observerlist.Add(new Baby()); // 小孩哭了
cat.MiaoObsever();

两者从本质上来说是没有什么太大的区别

第一种是通过多播委托来支持,是语法层面来支持;

第二种是通过面向对象的方法来完成,是程序设计层面来支持;

七、事件

发布者3:Cat.cs 【事件】

public event Action MiaoEventHanlder = null;public void MiaoEnvent()
{Console.WriteLine("{0} MiaoEnvent", this.GetType().Name);MiaoEventHanlder?.Invoke();//若event不为null,则invoke,这是C#6的新语法。 ?.称为空值传播运算符。
}
cat.MiaoEventHanlder += new Dog().Wang; //狗叫了
cat.MiaoEventHanlder += new Mouse().Run;//老鼠跑了
cat.MiaoEventHanlder += new Mother().Wispher;
cat.MiaoEnvent(); cat.MiaoEventHanlder.Invoke(); //不能执行

问题一:委托和事件有什么联系和区别?

  • 事件是委托的实例,事件是特殊的委托;

  • 事件和委托都可以使用 +=-= ;

问题二:既然有了多播委托,为什么又搞个事件呢?

安全

  • 事件是只能在类的内部执行,就算是你的子列也不能执行当前类中的事件,只能由自己来执行
  • 事件从外面操作,只能有一个 += / -=

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/4630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【2023 年第三届长三角高校数学建模竞赛】B 题 长三角新能源汽车发展与双碳关系研究 18页论文、数据和代码

【2023 年第三届长三角高校数学建模竞赛】B 题 长三角新能源汽车发展与双碳关系研究 18页论文、数据和代码 1 题目 《节能与新能源汽车技术路线图 2.0》提出至 2035 年&#xff0c;新能源汽车市场占比超过 50%&#xff0c;燃料电池汽车保有量达到 100 万辆&#xff0c;节能汽车…

【MySQL】MySQL在Centos7环境下安装

目录 一、卸载不要的环境 1.1、查看是否有安装mysql 1.2、关闭运行的程序 1.3、卸载安装 二、配置yum 源 2.1、下载yum 源 2.2 安装yum源 2.3 查看是否已经生效 三、安装mysql服务 四、启动服务 五、登录方法 方法一&#xff08;不行就下一个&#xff09; 方法二&#xff08;不…

动态sql语句

1.1 动态sql语句概述 Mybatis 的映射文件中&#xff0c;业务逻辑复杂时&#xff0c; SQL是动态变化的&#xff0c;此时在前面的学习中 SQL 就不能满足要求了。 参考的官方文档&#xff1a; 1.2 动态 SQL 之<if> 根据实体类的不同取值&#xff0c;使用不同的 SQL语句…

浅谈自动化测试工具 Appium

目录 前言&#xff1a; 一、简单介绍 &#xff08;一&#xff09;测试对象 &#xff08;二&#xff09;支持平台及语言 &#xff08;三&#xff09;工作原理 &#xff08;四&#xff09;安装工具 二、环境搭建 &#xff08;一&#xff09;安装 Android SDK &#xff0…

和chatgpt学架构02-环境搭建

目录 1 安装vs code2 vs code功能介绍3 安装nodejs4 安装vue5 在vs code打开工程总结 我们在上一篇 技术选型 里咨询了chatgpt前后端的框架选择和数据库的选择。有了框架之后就需要选择合适的开发工具了&#xff0c;继续咨询一下chatgpt 我现在选型&#xff0c;前端使用vue&am…

将媒体公司资产迁移到 Amazon S3 的技术方案

随着媒体公司的发展&#xff0c;他们在仓库中积累了大量的旧磁带和未数字化的视频。这些资产可能很有价值&#xff0c;但以目前的形式很难访问和货币化。此外&#xff0c;将这些资产存储在仓库中既有风险又昂贵。 媒体企业可以通过将其资产迁移到云存储来解决这些问题&#xf…

Lua 批量修改文件夹下文件名

local s io.popen("dir C:\\Users\\lizhiyuan\\Desktop\\国家知识产权局ftp法律状态数据\\data /b/s") local filelist s:read("*a")local start_pos 0while 1 do_,end_pos,line string.find(filelist, "([^\n\r].xml)", start_pos)if not e…

Java List中通过对象属性排序,可实现多条件排序

直接上代码&#xff1a; import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Data;import java.util.Comparator; import java.util.List; import java.util.stream.Collectors;/*** List 对象属性排序*/Data AllArgsConstructor clas…

xml.etree.ElementTree

python使用 xml.etree.ElementTree包的时候&#xff0c;对xml中的空标签进行了简写&#xff0c;想恢复成正常模式怎么弄

(学习笔记-TCP连接断开)TCP四次挥手

TCP四次挥手过程 TCP断开连接是通过四次挥手实现的&#xff0c;双方都可以主动断开连接&#xff0c;断开连接后主机中的资源将被释放&#xff0c;四次挥手的过程如下&#xff1a; 客户端打算关闭连接时&#xff0c;会发送一个TCP首部FIN标志位为1的报文&#xff0c;也就是FIN报…

基础语言模型LLaMA

LLaMA包含从7B到65B参数的基础语言模型集合。Meta在数万亿个tokens上训练了模型&#xff0c;LLaMA-13B在大多数基准测试中优于GPT-3&#xff08;175B&#xff09;。 来自&#xff1a;LLaMA: Open and Efficient Foundation Language Models 目录 背景概述方法预训练数据架构Op…

LangChain + Embedding + Chromdb,关联使用ChatGLM的本地搭建训练平台教程

一.介绍 OpenAI 在国内用户注册会遇到各种阻力&#xff0c;目前可行的方法是使用本地数据集的功能实现联网搜索并给出回答&#xff0c;提炼出TXT、WORD 文档里的内容。 现在主流的技术是基于强大的第三方开源库&#xff1a;LangChain 。 文档地址&#xff1a;&#x1f99c;…

基于单片机的教室智能照明台灯控制系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前时间、年月日、时分秒、前教室人数&#xff1b;2路红外探头用来感应当前教室进出人数&#xff1b;按键可以设置当前时间、自动手动模式、开启和关闭教室灯光时间&#xff1b;在手动模式下&#xff0c;可以通过按…

uni-app:单位像素rpx

rpx:响应式单位&#xff0c;长宽可以随着屏幕大小改变&#xff0c;其尺寸根据iPhone 6的标准来参考&#xff08;750rpx为占满屏幕宽&#xff0c;1334rpx为占满屏幕长。7501334rpx&#xff09; 例子&#xff1a; 现在展示型号为iPhone 12 pro的型号&#xff0c;这里750px和750…

51单片机学习--矩阵键盘、电子密码锁

矩阵键盘的模块原理图&#xff1a; 功能&#xff1a;按下矩阵按键&#xff0c;在LCD上显示对应的数值。 采用模块化编程&#xff0c;在此只给出MatrixKey.c 和 main.c 两部分代码 #include <REGX52.H> #include "Delay.h"/*矩阵键盘读取键码按下不放&#xf…

【有功功率、无功功率】可再生能源配电馈线的鲁棒经济调度研究[IEEE13节点](Matlab代码实现)

&#x1f4a5;1 概述 "有功功率和无功功率" 是与电力系统中能量传输和功率控制相关的两个重要概念。 有功功率&#xff08;Active Power&#xff09;是指电力系统中传输和消耗能量的功率&#xff0c;也被称为实功功率。它负责提供电力系统中的实际电能需求&#xf…

DTU凭什么可以为数据提供高效可靠的稳定传输?

当谈及到智能化的发展时&#xff0c;其实它已经正在影响着各行各业&#xff0c;当前许多传统行业也正在向智能化转变。如果你细心留意的话&#xff0c;就会发现在我们日常生活中多少都会见到它们的身影&#xff0c;例如&#xff1a;村庄各家各户墙外所挂的煤改电设备、居民楼小…

【数学建模】统计分析方法

文章目录 1.回归分析2. 逻辑回归3. 聚类分析4. 判别分析5. 主成分分析6. 因子分析7. 对应分析 1.回归分析 数据量要多&#xff0c;样本总量n越大越好——>保证拟合效果更好&#xff0c;预测效果越好 一般n>40/45较好 方法 建立回归模型 yiβ0β1i……βkxkiεi 所估计的…

IDEA使用GIT提交代码中文日志(commit message)乱码

最近换了新的开发环境&#xff0c;导致提交gti中文注释乱码&#xff0c;遂记录一下解决方案 idea中查看git提交信息显示中文是正常的 gitee上显示乱码 本地显示也是乱码 一、命令修改编码格式 git 安装目录下执行 git config --global i18n.commitencoding utf-8git config …

pdf怎么翻译?有这个工具就够了

pdf怎么翻译&#xff1f;PDF文档一直是我们日常生活和工作中不可避免的一部分。但是&#xff0c;当我们需要翻译PDF文件时&#xff0c;我们往往会感到无助&#xff0c;因为PDF文档不能像其他文本文件一样直接复制和粘贴。那么今天就给大家介绍一款可以帮助我们进行PDF翻译的工具…