百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C#语法——委托,架构师必修

bigegpt 2024-08-19 11:59 2 浏览

C#语法——委托,架构的血液

本篇文章主要介绍委托的应用。

委托是大家最常见的语法了,但会用与精通之间的差别是巨大的。

一个程序员如果不能精通委托,那么,他永远无法成为一个架构师。

所以,委托是必须掌握的技能之一。

委托的定义

什么是委托?

委托实际上是一种类型,是一种引用类型。

微软用delegate关键字来声明委托,delegate与int,string,double等关键字一样。都是声明用的。

下面先看下声明代码,这里声明了两个委托。

public delegate void TestDelegate(string message);
public delegate int TestDelegate(MyType m, long num);

delegate既然是关键字,和int,string一样,那么,为什么delegate后又跟了一个void或者int呢?

如果他们是同等地位的关键字,为什么可以一起使用呢?

很简单,我们把delegate后面的 【void TestDelegate(string message)】理解为一个变量,是不是就清晰明了了一些。

我们把delegate关键字理解为,是用来专门来定义这种复杂的变量的。而这种复杂的变量可以包含一个返回值和任意数目任意类型的传入参数。

有没有感觉,这个复杂的变量特别像一个函数的定义。

没错,官方定义,委托类型的声明与方法签名相似。所以,这个复杂变量,的确,书写的方式就是与函数一样。

那么,为什么这个声明方式如此怪异呢,是因为,我们用delegate定义的变量,只能用函数赋值。赋值方式如下所示:

public delegate void TestDelegate(string message);
public delegate long TestDelegate2(int m, long num);
public static void Excute()
{
 TestDelegate2 td = Double; 
} 
static long Double(int m, long num)
{
 return m * num;
}

委托的基本应用

学会了赋值以后,我开始使用委托。

委托的使用方式如下:

public static void Excute()
{
TestDelegate2 td = Double; 
string result = td(51, 8);
Console.WriteLine(result);
} 

这里我们会发现,委托的使用方式与函数调用一样。

没错,它们的确是一样的。因为委托是用函数来赋值的,所以调用方式一样也并不奇怪,不是吗。

换一种说法,就是委托封装了一个函数。

如果委托是封装的函数,并且它又是引用类型。那么委托第一种常规的应用就浮现出来了。

那就是——引用类型的函数。

如果函数是引用类型,那么这个函数只要没被内存回收,就可以被调用。如果是public函数或者是public static函数,那么它能跨越的东西就更多了。

比如可以跨类调用,跨程序集调用等等。而这种用法,就是委托的基本应用。

匿名委托的应用

匿名委托的官方介绍:在 2.0 之前的 C# 版本中,声明委托的唯一方式是使用命名方法。 C# 2.0 引入匿名方法,在 C# 3.0 及更高版本中,Lambda 表达式取代匿名方法作为编写内联代码的首选方式。

看不懂没关系,我们直接来学习使用。代码如下:

delegate string anonymousDelegate(int m, long num);
public static void Excute()
{
 anonymousDelegate ad = delegate (int m, long num) { return m.ToString() + num.ToString(); };//2.0时代的匿名委托

 anonymousDelegate ad2 = (m, num) => { return m.ToString() + num.ToString(); };//3.0以后匿名委托 
}

如代码所示,匿名委托也叫Lambda表达式,不懂的同学就当它是有固定写法即可,不用讲什么道理,只要记住并应用即可。

匿名委托虽然减少了一点代码,但还是要求我们自己去声明委托。所有,还能再简写一点吗?

答案当然是,可以的。

Action与Func

Action与Func是微软为我们预先定义好了的,两个委托变量。其中Action是不带返回值的委托,Func是带返回值的委托。

可以说,Action与Func完全包含了,我们日常使用所需的,全部的,委托变量。

也就是说,我们可以不用再去自己手动声明委托了。

下面来看最简单的Action与Func的定义:

Action a1 = () => { };
Func<int> f1 = () => { return 1; };//必须写 return 1;

Action与Func是泛型委托,最多支持16个入参变量。下面代码为一个入参的定义,多参数以此类推。

Action<int> a1 = (i) => { };
Func<string,int> f1 = (str) => { return 1;//必须写 return 1; };

委托的线程应用

委托的线程应用是委托的第二种用法,分为线程使用委托,和委托的异步应用两种。

我们先看线程使用委托。如下代码所示,一个无入参匿名Action和一个无入参匿名Func。

Task taskAction = new Task(() => { });//无入参匿名Action
taskAction.Start(); 

Task<int> taskFunc = new Task<int>(() => { return 1; });//无入参匿名Func
taskFunc.Start();

int result= taskFunc.GetAwaiter().GetResult();//获取线程返回结果

我们能看到两种委托应用,代码都非常简洁。

下面我们再来看委托的异步应用。首先看最简单的异步调用。

Action action = new Action(() => { });
IAsyncResult result = action.BeginInvoke((iar) =>
{
}, null);
 
Func<int> func = new Func<int>(() => { return 1; }); 
IAsyncResult resultfunc = func.BeginInvoke((iar) =>
{
 var res = func.EndInvoke(iar); 
}, null);

这里我们使用委托的BeginInvoke方法来开启线程,进行异步调用。如上面代码所示,这里介绍了Action与Func的最基础的异步应用。

委托是架构的血液,如果系统中没有委托,那代码将堆叠到一起,耦合性就很高。

就好比一碗汤面倒掉了所有的汤,只要它静放一个阵子,就会变成一坨面球,让你无从下嘴。

所以,委托是是框架的流畅的基石。

那么委托到底是如何流动的呢?

我们先从刚介绍过的委托的线程应用说起。

第一核心应用——随手线程:

我们在做开发的时候,一定接触过父类。父类是干什么的呢?父类通常是用来编写公共属性和函数,方便子类调用的。

那我们的委托的第一个核心应用,就是父类的公共函数,线程随手启动。如何随手开启呢?

首先,我们创建父类代码如下:

class BaseDelegateSyntax
{ 
 public void AsyncLoad(Action action)
 {
 
 }
 public void AsyncLoad(Action action, Action callback)
 {
 IAsyncResult result = action.BeginInvoke((iar) =>
 {
 callback();
 }, null);
 }

 public void AsyncLoad<T>(Action<T> action, T para, Action callback)
 {
 IAsyncResult result = action.BeginInvoke(para, (iar) =>
 {
 callback();
 }, null);
 }

 public void AsyncLoad<T, R>(Func<T, R> action, T para, Action<R> callback)
 {
 IAsyncResult result = action.BeginInvoke(para, (iar) =>
 {
 var res = action.EndInvoke(iar);
 callback(res);
 }, null);
 }
}

我们看到上面的代码,父类中添加了四个异步委托的调用函数,接下来,我们就可以在继承该类的子类中,随手开启线程了。

子类代码如下:

class ChildDelegateSyntax : BaseDelegateSyntax
{
 public void Excute()
 {
 //开启异步方法
 base.AsyncLoad(() => { });
 
 //开启异步方法,并且在异步结束后,触发回调方法
 base.AsyncLoad(() => { },
 ()=> 
 {
 //我是回调方法
 });
 
 //开启异步有入参的方法,传递参数,并且在异步结束后,触发回调方法
 base.AsyncLoad<string>((s) => { },"Kiba518",
 () =>
 {
 //我是回调方法
 });
 
 //开启异步有入参的方法,传递字符串参数Kiba518,之后返回int型结果518,
 //并且在异步结束后,触发回调方法,回调函数中可以获得结果518
 base.AsyncLoad<string,int>((s) => {
 return 518;
 }, "Kiba518",
 (result) =>
 {
 //我是回调方法 result是返回值518
 });
 }
}

看了上面的父子类后,是否感觉委托让我们繁杂的线程世界变简洁了呢?

第二核心应用——穿越你的世界:

接下来,我们来看委托的第二种核心用法,穿越的应用。

这个应用,是最常见,也最普通的应用了。因为委托是引用类型,所以A类里定义的委托,可以在被内存回收之前,被其他类调用。

我们经常会在各种论坛看到有人发问,A页面如何调用B页面的属性、方法、父页面获取子页面的属性、方法,或者子页面获取父页面的属性、方法。

其实,只要定义好委托,并将委托正确的传递,就可以实现穿越的调用了。

下面我们看下穿越应用的代码。

public class FirstDelegateSyntax
{
 public FirstDelegateSyntax()
 {
 Console.WriteLine(" First 开始 " );
 SecondDelegateSyntax sds = new SecondDelegateSyntax(()=> {
 Console.WriteLine(" First传给Second委托被触发 ");
 });
 sds.Excute();
 Console.WriteLine(" First 结束 ");
 }
}
 
public class SecondDelegateSyntax
{
 public Action Action { get; set; }
 public SecondDelegateSyntax(Action _action)
 {
 Console.WriteLine(" Second的构造函数 ");
 Action = _action;
 }
 public void Excute()
 {
 Console.WriteLine(" Second的Excute被触发 ");
 Action();
 }
}

我们可以看到,我们传递的委托,穿越了自身所属的类。在SecondDelegateSyntax类中被触发了。

运行结果如下:

第三核心应用——回调函数:

世界上本没有回调函数,叫的人多了,也就有了。

请记住,所有的回调函数,都是委托的穿越应用,所有的回调函数;都是委托的穿越应用;所有的回调函数,都是委托的穿越应用。

重要的话要讲三遍。

因为委托是引用类型,所以可以被[址传递]。函数是不可以被传递的。

当你传递函数的时候,其实是匿名传递了一个委托的地址。

结语

委托是我们最常用的语法,它将函数封装成引用类型的变量,供其他单位调用。

因为委托的特质是引用类型,所以决定了委托是可以进行址传递。也就是说,委托是穿梭于我们系统代码中的列车。

我们可以在列车上放很多很多东西,在需要的站点,叫停列车,并将托运的东西搬下来使用。

所以,理论上,只要我们利用好委托,就可以大量减少冗余的代码。

但委托这种列车,是每个程序员都可以定义的,如果一个项目中有十个开发者,每个人都在定义委托,那么,就有可能出现定义了十个相同的委托的情况,这样就出现了撞车的现象。

所以委托在使用的时候,尽量做到有序传递,即预先做好列车的行驶路线,让委托按照路径运行,尽量不要定义可以被任何单位调用的公共委托。

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...