本篇文章主要介绍委托的应用。html
委托是你们最多见的语法了,但会用与精通之间的差异是巨大的。程序员
一个程序员若是不能精通委托,那么,他永远没法成为高级程序员。架构
因此,让咱们把委托刻到血液里吧。框架
这样,你才能称为[Developer]。异步
委托的定义async
什么是委托?函数
委托其实是一种类型,是一种引用类型。学习
微软用delegate关键字来声明委托,delegate与int,string,double等关键字同样。都是声明用的。spa
下面先看下声明代码,这里声明了两个委托。线程
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; }
委托的基本应用
学会了赋值之后,我开始使用委托。
委托的使用方式以下:
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类中被触发了。
运行结果以下:
第三核心应用——回调函数:
世界上本没有回调函数,叫的人多了,也就有了。
请记住,全部的回调函数,都是委托的穿越应用,全部的回调函数;都是委托的穿越应用;全部的回调函数,都是委托的穿越应用。
重要的话要讲三遍。
由于委托是引用类型,因此能够被[址传递]。函数是不能够被传递的。
当你传递函数的时候,实际上是匿名传递了一个委托的地址。
结语
委托是咱们最经常使用的语法,它将函数封装成引用类型的变量,供其余单位调用。
由于委托的特质是引用类型,因此决定了委托是能够进行址传递。也就是说,委托是穿梭于咱们系统代码中的列车。
咱们能够在列车上放不少不少东西,在须要的站点,叫停列车,并将托运的东西搬下来使用。
因此,理论上,只要咱们利用好委托,就能够大量减小冗余的代码。
但委托这种列车,是每一个程序员均可以定义的,若是一个项目中有十个开发者,每一个人都在定义委托,那么,就有可能出现定义了十个相同的委托的状况,这样就出现了撞车的现象。
因此委托在使用的时候,尽可能作到有序传递,即预先作好列车的行驶路线,让委托按照路径运行。尽可能不要定义能够被任何单位调用的公共委托。
若是须要公共委托,能够采起反射的方式来调用。
后面我会继续写事件,消息,反射等语法,敬请期待。
----------------------------------------------------------------------------------------------------
注:此文章为原创,任何形式的转载都请联系做者得到受权并注明出处!
若您以为这篇文章还不错,请点击下方的【推荐】,很是感谢!