++分析猫和老鼠示例数据库
++++假若有一只猫叫Tom,有两只老鼠Jerry和Jack,Tom只要叫一声喵,我是Tom。 两只老鼠就会说,老猫来了,快跑。编程
++++分析上面的案例,有几个类,如何处理类与类之间的关系?小程序
++++猫和老鼠设计模式
-- 1、至少存在两个类,Cat和Mouse网络
-- 2、在Cat类中存在一个方法,叫Shout。ide
-- 3、当Shout方法触发时,Mouse类应该执行某个方法,好比Run。函数
--显然猫和老鼠并不认识,不会主动通知老鼠我来了。 那么老鼠应该是监听猫的行为,当猫的行为触发了,老鼠天然就知道了。学习
++事件的引入动画
++++在Cat类中,不该该关联Mouse类。此时委托事件的方式就是最好的处理办法,下面咱们建立一个Cat类,部分代码以下:this
private string name;
public Cat(string name){
this.name = name;
}
public delegate void CatShoutEventHandler();
public event CatShoutEventHandler CatShout;
++事件的定义
++++事件是说在发生其余类或对象关注的事情时,类或对象可经过事件通知它们。 事件的关键字是event。
public event CatShoutEventHandler CatShout;
这行代码的意思是声明事件CatShout,它的事件类型是委托CatShoutEventHandler。
正如上节课咱们提到的,委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具备彻底相同的行为。
++++Shout部分代码
public void Shout(){
Console.WriteLine(“喵,我是{}!”, name);
if(CatShout != null){ //表示当执行Shout方法时,若是CatShout中有对象登记事件,则执行
CatShout();
}
}
调用事件与调用委托是同样的,这里须要注意CatShout()之因此不带任何参数,是由于CatShout的事件类型是委托CatShoutEventHandler(),而它自己是无参数,无返回值的。
++++Mouse部分代码:
private string name;
public Mouse(string name){
this.name = name;
}
public void Run(){
Console.WriteLine(“老猫来了,{0}快跑!”, name);
}
++++Main函数部分代码
Cat cat = new Cat(“Tom”);
Mouse m1 = new Mouse(“Jerry”);
Mouse m2 = new Mouse(“Jack”);
cat.CatShout += new Cat.CatShoutEventHandler(m1.Run);
cat.CatShout += new Cat.CatShoutEventHandler(m2.Run);
cat.Shout();
Console.Read();
代码分析:咱们先来看看在咱们声明委托的时候,这句代码的意义:
public delegate void CatShoutEventHandler();
这行代码里面咱们用的修饰符是public,众所周知意味着公开的意思,任何外部存在的类均可以进行赋值(绑定委托时使用=号)。在某种程度上,委托破坏了对象的封装性。
++如何封装委托
++++你是猴子请来的逗逼吗?
为了保证不破坏对象的封装性,咱们将public修改成private。(这特么简直就是在搞笑!)
由于声明委托的目的就是为了把它暴露在类的客户端进行方法的注册,让外部帮我去作事情的。你把它声明为private了,客户端对它根本就不可见,那它还叫委托吗?
++++那如何封装委托呢?
咱们刚接触面向对象的时候,介绍过字段通常推荐用private修饰,为了防止外界破坏类的内部结构,同时为了外界能访问到私有属性,咱们引入属性这一律念。 即面向对象三大特性之一的封装性。
因而,Event出场了,它封装了委托类型的变量。 使得: 在类的内部,无论你声明它是public仍是protected,它老是private的。 在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
++++为了验证这个问题,咱们修改了Main函数部分代码:
Cat cat = new Cat(“Tom”);
Mouse m1 = new Mouse(“Jerry”);
Mouse m2 = new Mouse(“Jack”);
//这里咱们修改了+=为=
cat.CatShout = new Cat.CatShoutEventHandler(m1.Run);
cat.CatShout += new Cat.CatShoutEventHandler(m2.Run);
cat.Shout();
Console.Read();
运行程序,会出现编译错误
!!!CS 0070事件Cat.CatShout只能出如今+=或-=的左边(从类型”Cat”中使用时除外)
编译错误很清晰的告诉咱们除了在类的内部,咱们不能用除了+=符号来绑定事件,固然也能够经过反编译去查看源码(后面高级课咱们会介绍如何利用Refector反编译源代码)
++++事件其实没有什么很差理解的,声明一个事件不过相似于声明一个进行了封装的委托类型的变量而已。
++总结
++++1、事件是一种特殊的委托的实例,或者说是受限制的委托,是委托一种特殊应用,在类的外部只能施加+=,-=操做符,两者本质上是一个东西。
++++2、事件只容许用add(+=),remove(-=)方法来操做,这致使了它不容许在类的外部被直接触发,只能在类的内部适合的时机触发。 委托能够在外部被触发,可是别这么用。 使用中,委托经常使用来表达回调,事件表达外发的接口。
++++3、事件不能够看成形参传递,可是委托能够。
++思考一个问题,如何在猫来的时候让老鼠知道是哪只猫来了呢?
++++这时候咱们须要定义一个类,这个类继承自EventArgs。
++++EventArgs是包含事件数据的类的基类。换句话说见明知意,这个类的做用就是用来在事件触发时传递数据的。(固然也能够用别的方法实现后面会介绍)
++++新建类CatShoutEventArgs,代码以下:
class CatShoutEventArgs : EventArgs{
private string name;
public string Name{
set{ name = value; }
get{return name; }
}
}
++++修改Cat类部分代码:
-- 1、修改委托
public delegate void CatShoutEventHandler(object sender,CatShoutEventArgs args);
-- 2、修改Shout中部分代码:
CatShoutEventArgs args =new CatShoutEventArgs();
args.Name = this.name;
CatShout(this, args);
++++修改Mouse类部分代码:
public void Run(object sender,CatShoutEventArgs args){
Console.WriteLine(“老猫{0}来了,{1}快跑!”, args.Name, name);
}
++++运行程序,你会发现结果和以前显示的不同了! 咱们经过一个类去传递事件发生时想要传递的参数,对于客户端(Main)方法咱们一句代码都没有动过!
咱们修改了代码,客户端没有更改就改变告终果。换句话说,对于咱们内部的类来讲修改没有影响到别的类,这就是必定程度上的松耦合。
++猫和老鼠案例总结
++++经过猫和老鼠的案例,咱们学习了事件的用法。 其实这个案例是典型的观察者设计模式,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系(一只猫多个老鼠),以便于当一个对象的状态改变时,其余依赖于它的对象会被自动告知并更行。 Observer模式是一种松耦合的设计模式。
++Observer设计模式
++++观察者模式又叫 发布-订阅模式(Publish/Subscribe)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态发生变化时,会通知全部的观察者对象,是他们可以自动更新本身。
++++观察者模式通常会牵扯至少两个角色
Subject或Publish,叫作具体主体或通知者,将全部有关状态存入具体观察者对象;在具体主题的内部状态改变时,给全部登记过的观察者发出通知。
observer或subscribe类,具体观察者。
++++在猫与老鼠的案例中,猫是发布者,老鼠尾订阅者。 当猫的某个状态发生改变时(这里是方法触发时),订阅者即老鼠收到了相关通知,去改变自身的行为。
++Observer模式的特色
++++当一个对象的改变须要同时改变其余对象的时候,而且它不知道具体有多少对象有待改变时,应该采用观察者模式。
++++观察者模式所作的工做其实就是在解耦合。让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另外一边的变化。
++Observer模式最终案例
++++玩手机的同窗,代码以下:
class PlayObserver{
private string name;
private Subject sub;
public PlayObserver(string name,Subject sub){
this.name = name;
this.sub = sub;
}
//放下玩手机的首,在伪装写代码
public void PlayPhoneToCode(){
Console.WriteLine(“{0} {1}放下手机,伪装在写代码”, sub.SubjectState, this.name);
}
}
++++看视频的同窗,代码以下:
class ViewObserver{
private string name;
private Subject sub;
public ViewObserver(string name,Subject sub){
this.name = name;
this.sub = sub;
}
//放下玩手机的手,在伪装写代码
public void WatchVideoToCode(){
Console.WriteLine(“{0} {1}关掉视频,切换到写代码”, sub.SubjectState, this.name);
}
}
++++客户端,部分代码以下:
Boss ylzServ =new Boss();
PlayObserver lisi =new PlayObserver(“李四”, ylzServ); //玩手机的同窗
ylzServ.Update += new EventHandler(lisi.PlayPhoneToCode); //监听
ylzServ.SubjectState = “钻钻来查班了!”; //老师来了
ylzServ.Notify(); //发出通知
Console.Read();
++事件委托说明
++++1、委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具备彻底相同的行为。
++++2、委托方法的使用能够像其余任何方法同样,具备参数和返回值。
++++3、委托能够看做是对函数的抽象,是函数的“类”,委托的实例将表明一个具体的函数。
++++4、一个委托能够搭载多个方法,全部方法被依次唤起,更重要的是,它可使委托对象搭载的方法不须要属于同一个类。
++.Net Framework中的委托与事件
++++咱们先搞懂.Net Framework的编码规范:
--委托类型的名称都应该以EventHandler结束。
--委托的原型定义: 有一个void返回值,并接受两个输入参数: 一个Object类型,一个EventArgs类型(或继承自EventArgs)。
--事件的命名为: 委托去掉EventHandler以后剩余的部分。
--继承自EventArgs的类型应该以EventArgs结尾。
++++再作一下说明:
--1、委托声明原型中的Object类型的参数表明了Subject,也就是监视对象,在本例中是Heater(热水器)。 回调函数(好比Alarm的MakeAlert)能够经过它访问触发事件的对象(Heater)。
--2、EventArgs对象也包含了Observer所感兴趣的数据,在本例中是temperature。
++++上面描述不只仅是为了编码规范而已,这样也使得程序有更大的灵活性。(好比说,若是咱们不光想得到热水器的温度,还想再Observer段(警报器或者显示器)方法中得到它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而若是咱们将热水器的引用传给警报器的方法,就能够在方法中直接访问热水器了)。
++++一个符号.Net Framework规范的示例:
using System;
using System.Collections.Generic;
using System.Text;
namespace Delegate{
//热水器
public class Heater{
private int temperature;
public string type =“VRunSoft 001”; //添加型号做为演示
public string area =“China Beijing”; //添加产地做为演示
//声明委托
public delegate void BoiledEventHandler(Objectsender,BoildEventArgse);
public event BoiledEventHandler Boiled; //声明事件
//定义BoiledEventArgs类,传递给Observer所感兴趣的信息
public class BoiledEventArgs : EventArgs{
public readonly int temperature;
public BoiledEventArgs(int temperature){
this.temperature = temperature;
}
}
//能够供继承自Heater的类重写,以便继承类拒绝其余对象对它的监视
protected virtual void OnBoiled(BoiledEventArgse){
if(Boiled != null){ //若是有对象注册
Boiled(this, e); //调用全部注册对象的方法
}
}
//烧水
public void BoilWater(){
for(int i = 0; i <= 100; i++){
temperature = i;
if(temperature > 95){
//创建BoiledEventArgs对象
BoiledEventArgse = new BoiledEventArgs(temperature);
OnBoiled(e); //调用OnBolied方法
}
}
}
}
//警报器
public class Alarm{
public void MakeAlert(Object sender, Heater.BoiledEventArgs e){
Heater heater = (Heater)sender; //这里是否是很熟悉呢?
//访问sender中的公共字段
Console.WriteLine(“Alarm: {0}-{1}:”, heater.area, heater.type);
Console.WriteLine(“Alarm:滴滴滴,水已经{0}度了:”, e.temperature);
Console.WriteLine();
}
}
//显示器
public class Diplay{
public static void ShowMsg(Object sender,Heater.BoiledEventArgs e){//静态方法
Heaterheater = (Heater)sender;
Console.WriteLine(“Display:{0}-{1}:”, heater.area, heater.type);
Console.WriteLine(“Display:水快烧开了,当前温度:{0}度。”, e.temperature);
Console.WriteLine();
}
}
class Program{
static void Main(){
Heater heater = new Heater();
Alarm alarm = new Alarm();
heater.Boiled += alarm.MakeAlert; //注册方法
heater.Boiled += (new Alarm()).MakeAlert; //给匿名对象注册方法
heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert); //也能够这么注册
heater.Boiled += Display.ShowMsg; //注册静态方法
heater.BoilWater(); //烧水,会自动调用注册过对象的方法。
}
}
}
输出为: ....
++10、总结
++++在本文中咱们首先经过一个GreetingPeople的小程序向你们介绍了委托的概念、委托用来作什么,随后又引出了事件,接着对委托与事件所产生的中间代码作了粗略的讲述。
++++在第二个稍微复杂点的热水器的范例中,咱们简要介绍了Observer设计模式,并经过实现这个范例完成了该模式,随后讲述了.Net Framework中委托、事件的实现方法。
#立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++立钻哥哥推荐的拓展学习连接(Link_Url):
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++C#事件:http://www.javashuo.com/article/p-zmwruvql-gm.html
++++C#委托:http://www.javashuo.com/article/p-uozpymaf-gh.html
++++C#集合:http://www.javashuo.com/article/p-sfqfdqsf-ex.html
++++C#泛型:http://www.javashuo.com/article/p-xrttqngo-ee.html
++++C#接口:http://www.javashuo.com/article/p-vhlfplgv-dm.html
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:http://www.javashuo.com/article/p-olslkfao-cq.html
++++C#数据类型:http://www.javashuo.com/article/p-hmabbtmc-ba.html
++++Unity3D默认的快捷键:http://www.javashuo.com/article/p-wuwcrclr-s.html
++++游戏相关缩写:http://www.javashuo.com/article/p-mwacxwca-gm.html
++++Unity引擎基础:http://www.javashuo.com/article/p-beommoeb-ka.html
++++Unity面向组件开发:http://www.javashuo.com/article/p-eigmuvut-dt.html
++++Unity物理系统:http://www.javashuo.com/article/p-nqvvciwv-kd.html
++++Unity2D平台开发:http://www.javashuo.com/article/p-ycaagdtj-hs.html
++++UGUI基础:http://www.javashuo.com/article/p-rukxwckw-mc.html
++++UGUI进阶:http://www.javashuo.com/article/p-wcatruhq-gt.html
++++UGUI综合:http://www.javashuo.com/article/p-dkccmqii-gg.html
++++Unity动画系统基础:http://www.javashuo.com/article/p-mbrdouxy-dq.html
++++Unity动画系统进阶:http://www.javashuo.com/article/p-aqaqpbkh-bp.html
++++Navigation导航系统:http://www.javashuo.com/article/p-dswwllas-t.html
++++Unity特效渲染:http://www.javashuo.com/article/p-ckojjyfj-bp.html
++++Unity数据存储:http://www.javashuo.com/article/p-bvlzynso-m.html
++++Unity中Sqlite数据库:http://www.javashuo.com/article/p-ejutsbxl-ca.html
++++WWW类和协程:http://www.javashuo.com/article/p-dbwmhsav-cy.html
++++Unity网络:http://www.javashuo.com/article/p-sqrlntgh-dw.html
++++设计模式简单整理:http://www.javashuo.com/article/p-rngqugib-hg.html
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++UML类图:http://www.javashuo.com/article/p-sxberuew-bm.html
++++Unity知识点0001:http://www.javashuo.com/article/p-ryvdxxjr-ep.html
++++U3D_Shader编程(第一篇:快速入门篇):http://www.javashuo.com/article/p-kyppgrac-gz.html
++++U3D_Shader编程(第二篇:基础夯实篇):http://www.javashuo.com/article/p-qkyowtli-hv.html
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft:lovezuanzuan--_--