我们先无论这个标题怎样的绕口。也无论托付到底是个什么东西,来看以下这两个最简单的方法,它们只是是在屏幕上输出一句问候的话语:
如果这个程序须要进行全球化。哎呀,不好了,我是中国人,我不明确”Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法:
这时候。GreetPeople也须要改一改了,不然怎样推断究竟用哪个版本号的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为推断的根据:
OK。虽然这样攻克了问题。但我不说大家也非常easy想到,这个解决方式的可扩展性非常差,假设日后我们须要再加入韩文版、日文版,就不得不重复改动枚举和GreetPeople()方法,以适应新的需求。
在考虑新的解决方式之前,我们先看看 GreetPeople的方法签名:
public void GreetPeople(string name, Language lang)
MakeGreeting(name);
好了,有了思路了,我们就来改改GreetPeople()方法。那么它应该是这个样子了:
注意到 。这个位置通常放置的应该是參数的类型,但到眼下为止。我们不过想到应该有个能够代表方法的參数,并按这个思路去改写GreetPeople方法,就出现了一个大问题: *这个代表着方法的MakeGreeting參数应该是什么类型的?
NOTE:这里已不再须要枚举了,由于在给MakeGreeting赋值的时候动态地决定使用哪个方法,是ChineseGreeting还是 EnglishGreeting,而在这个两个方法内部。已经对使用”morning”还是”早上好”作了区分。
聪明的你应该已经想到了,是托付该出场的时候了。但讲述托付之前,我们再看看MakeGreeting參数所能代表的 ChineseGreeting()和EnglishGreeting()方法的签名:
于是,托付出现了: 它定义了MakeGreeting參数所能代表的方法的种类,也就是MakeGreeting參数的类型。
NOTE:假设上面这句话比較绕口。我把它翻译成这样:string 定义了name參数所能代表的 值的种类,也就是name參数的类型。
本例中托付的定义:
public delegate void GreetingDelegate(string name);
能够与上面EnglishGreeting()方法的签名对照一下,除了增加了delegatekeyword以外,其余的是不是全然一样?
让我们再次修改GreetPeople()方法,例如以下所看到的:
输出例如以下:
Morning, Jimmy Zhang
早上好, 张子阳
我们对托付做一个总结:
看到这里。是不是有那么点如梦初醒的感觉?于是,你是不是在想:在上面的样例中,我不一定要直接在GreetPeople()方法中给 name參数赋值,我能够像这样使用变量:
而既然托付GreetingDelegate 和 类型 string 的地位一样,都是定义了一种參数类型,那么,我是不是也能够这么使用托付?
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
实际上。我们也能够绕过GreetPeople方法。通过托付来直接调用EnglishGreeting和ChineseGreeting:
NOTE:这在本例中是没有问题的,但回头看下上面GreetPeople()的定义,在它之中能够做一些对于EnglishGreeting和ChineseGreeting来说都须要进行的工作,为了简便我做了省略。
我们也能够使用以下的代码来这样简化这一过程:
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此托付变量再绑定一个方法
看到这里,应该注意到,这段代码第一条语句与实例化一个类是何其的相似。你不禁想到:上面第一次绑定托付时不能够使用”+=”的编译错误,也许能够用这个方案来避免:
GreetingDelegate delegate1 = new GreetingDelegate();
delegate1 += EnglishGreeting; // 这次用的是 “+=”,绑定语法。
delegate1 += ChineseGreeting; // 给此托付变量再绑定一个方法
既然给托付能够绑定一个方法,那么也应该有办法取消对方法的绑定,非常easy想到。这个语法是”-=”:
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
早上好, 张子阳
让我们再次对托付作个总结:
我们继续思考上面的程序:上面的三个方法都定义在Program类中,这样做是为了理解的方便,实际应用中。通常都是 GreetPeople 在一个类中,ChineseGreeting和 EnglishGreeting 在另外的类中。你已经对托付有了初步了解,是时候对上面的样例做个改进了。如果我们将GreetingPeople()放在一个叫GreetingManager的类中,那么新程序应该是这个样子的:
这个时候,假设要实现前面演示的输出效果,Main方法我想应该是这种:
Morning, Jimmy Zhang
早上好, 张子阳
如果我们须要使用上一节学到的知识,将多个方法绑定到同一个托付变量,该怎样做呢?让我们再次改写代码:
输出:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
我们能够这样使用这个托付变量:
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
虽然这样做没有不论什么问题,但我们发现这条语句非常奇怪。在调用gm.GreetPeople方法的时候,再次传递了gm的delegate1字段:
gm.GreetPeople(“Jimmy Zhang”, gm.delegate1);
既然如此,我们何不改动 GreetingManager 类成这样:
在client,调用看上去更简洁一些:
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
虽然这样达到了我们要的效果,可是还是存在着问题:
在这里,delegate1和我们平时用的string类型的变量没有什么分别,而我们知道,并非全部的字段都应该声明成public,合适的做法是应该public的时候public,应该private的时候private。
我们先看看假设把 delegate1 声明为 private会如何?结果就是: 这简直就是在搞笑。由于声明托付的目的就是为了把它暴露在类的client进行方法的注冊。你把它声明为private了,client对它根本就不可见。那它还有什么用?
再看看把delegate1 声明为 public 会如何?结果就是: 在client能够对它进行任意的赋值等操作,严重破坏对象的封装性。
最后。第一个方法注冊用”=”。是赋值语法。由于要进行实例化,第二个方法注冊则用的是”+=”。可是。不 管是赋值还是注冊,都是将方法绑定到托付上。除了调用时先后顺序不同,再没有不论什么的分别。这样不是让人认为非常别扭么?
我们想想。假设delegate1不是一个托付类型。而是一个string类型,你会怎么做? 答案是使用属性对字段进行封装。
我们改写GreetingManager类,它变成了这个样子:
为了证明上面的推论。假设我们像以下这样改写Main方法:
会得到编译错误:事件”Delegate.GreetingManager.MakeGreet”仅仅能出如今 += 或 -= 的左边(从类型”Delegate.GreetingManager”中使用时除外)。
这时候,我们凝视掉编译错误的行。然后又一次进行编译,再借助Reflector来对 event的声明语句做一探究,看看为什么会发生这种错误:
public event GreetingDelegate MakeGreet;
能够看到。实际上虽然我们在GreetingManager里将 MakeGreet 声明为public,可是,实际上MakeGreet会被编译成 私有字段。难怪会发生上面的编译错误了。由于它根本就不同意在GreetingManager类的外面以赋值的方式訪问,从而验证了我们上面所做的推论。
我们再进一步看下MakeGreet所产生的代码:
关于这个类的更深入内容,能够參阅《CLR Via C#》等相关书籍,这里就不再讨论了。
输出为:
Alarm:嘀嘀嘀,水已经 96 度了:
Alarm:嘀嘀嘀,水已经 96 度了:
Display:水快烧开了,当前温度:96度。
// 省略…
我们须要写个程序来模拟这个烧水的过程,我们将定义一个类来代表热水器,我们管它叫:Heater,它有代表水温的字段。叫做temperature。当然。还有不可缺少的给水加热方法BoilWater(),一个发出语音警报的方法MakeAlert(),一个显示水温的方法。ShowMsg()。
上面的样例显然能完毕我们之前描写叙述的工作,可是却并不够好。如果热水器由三部分组成:热水器、警报器、显示器。它们来自于不同厂商并进行了组装。那么。应该是 热水器只负责烧水。它不能发出警报也不能显示水温;在水烧开时由 警报器发出警报、 显示器显示提示和水温。
这时候。上面的样例就应该变成这个样子:
这里就出现了一个问题:怎样在水烧开的时候通知报警器和显示器?在继续进行之前,我们先了解一下Observer设计模式,Observer设计模式中主要包含例如以下两类对象:
在回答上面的问题之前,我们先搞懂 .Net Framework的编码规范:
输出为:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀,水已经 96 度了:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀。水已经 96 度了:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀。水已经 96 度了:
Display:China Xian – RealFire 001:
// 省略 …
在本文中我首先通过一个GreetingPeople的小程序向大家介绍了托付的概念、托付用来做什么,随后又引出了事件,接着对托付与事件所产生的中间代码做了粗略的讲述。
希望这篇文章能给你带来帮助。
Original: https://www.cnblogs.com/cynchanpin/p/7403780.html
Author: cynchanpin
Title: 托付
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/551484/
转载文章受原作者版权保护。转载请注明原作者出处!