一直弄不明白这些东西。直到看到这个博客:http://www.cnblogs.com/guodapeng/archive/2010/07/08/1773307.html
1.什么时候用委托
Task类里面,Search和ComplexSearch下都有“根据不同平台来进行不同动作”的行为。现在的代码是这样:
TaskComplexSearch(string taskPlatform) { if taskPlatform =="aaa" {...} if taskPlatform=="bob" {...} }
这样就需要用到很多if else语句,或者switch case语句。
用委托就可以避免这种情况。委托就是把方法当做一种变量,定义之后可以当做参数传递。之后凡是和这个委托有相同类型和参数的方法都可以进行传递。
就像Do(string AAA)之后所有满足string类型的都可以传递到这个函数里。委托只是又多了一个条件:还有一个参数的类型也需要相同。
声明一个委托,delegate void SearchDifferentPlatform(string keyword)
同时有针对不同平台写的方法:
public void SearchAAA(string aaa)
public void SearchBBB(string bbb)
...
【注意上面这两个方法,和委托一样都是void类型,和委托一样都有一个string的参数。因此是可以当做参数传递的。】
之前的代码写成委托的形式是这样:
TaskComplexSearch(string keyword, SearchDifferentPlatform searchDiffPlatform) { searchDiffPlatform(keyword); }
对这个函数的调用就是这样:
TaskComplexSearch("找一下aaa",SearchAAA); TaskComplexSearch("找一下bbb", SearchBBB);
委托看上去和变量是一样的,但是有一些不同点:除了赋值外,委托还可以用绑定的方式来调用。就是这两个东西:
+=
-=
如果有多次的绑定,在调用的时候将依次进行:
Delegate 1 +=xxxx;
Delegate 2+=yyyy;
//调用一次
//输出:依次调用两次的结果
2.什么时候用方法
专业一点的代码结构是,委托的定义和调用应该分开在两个类里面。类似MFC的构架。因此工程中代码应该是这样:
//委托所在的类 public class myDelegate() { TaskComplexSearch(string keyword, SearchDifferentPlatform searchDiffPlatform) { searchDiffPlatform(keyword); } } //调用函数所在的类 public class DoSomething() { public void SearchAAA(string xxx)... public void SearchBBB(string yyy)... }
调用的时候就是在主函数里实例化一个委托类:
myDelegate D1=new myDelegate();
D1.TaskComplexSearch("aaa",SearchAAA);
上面的例子,如果想看上去专业一点,不应该有第二个参数(就是SearchAAA)。该怎么做到呢?这里通过一系列的演变来说明:
这是委托最初的样子:(委托是委托,类是类)
//定义委托,它定义了可以代表的方法的类型 public delegate void GreetingDelegate(string name); //新建的GreetingManager类 public class GreetingManager{ public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } }
然后委托进入类里进行了封装:
public class GreetingManager{ //在GreetingManager类的内部声明delegate1变量 public GreetingDelegate delegate1; public void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } }
我们不想在调用时还包含第二个参数,因此把GreetPeople方法写成:
public class GreetingManager{ //在GreetingManager类的内部声明delegate1变量 public GreetingDelegate delegate1; public void GreetPeople(string name) { if(delegate1!=null){ //如果有方法注册委托变量 delegate1(name); //通过委托调用方法 } } }
这样就将一个委托封装进了方法里面,而且调用时只需要一个参数即可。
将这个概念用到工程里,就是
public class TaskComplexSearch{ public TaskComplexSearchDelegate delegate; public void GoTaskSearch(string keyword) { if(delegate !=null) { delegate (keyword); } } }
调用的时候:实例化一个TaskComplexSearch类A,然后
A.GoTaskSearch(keyword);
就行了。
可是问题是,这个delegate不能用public来修饰。否则会让整个类的封装性能大打折扣。外部风暴可以随意入侵,nonono。
但是delegate用private修饰的话,外部又没法调用了。那咋办嘞?
原博是这句话:“现在我们想想,如果delegate1不是一个委托类型,而是一个string类型,你会怎么做?答案是使用属性对字段进行封装”
其实就算换成值类型我也不知道该怎么办。而且我也不懂“使用属性对字段进行封装”是个啥。
后来想想,应该就是get set之类,通过属性来调用一个private的类的内部东东。
换成委托,我们改用event这个东西。概念和上面那个get set是一样的。
public class GreetingManager{
//这一次我们在这里声明一个事件
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name) {
MakeGreet(name);
}
}
这样修改之后,event虽然定义为一个public,但编译仍然为private。在调用的时候有可能会出现各种情况,这里先留下,在后面修改工程代码之后再总结。
3.Observer设计模式
这是个新的名词,至少对我来说是的。Observer设计模式大概是这样:
(1)有一个监视对象
(2)有一个监视器
Observer是一种松耦合的模式,在监视对象状态更新的时候,监视器可以发现,并进行相应的动作。
“Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。”
比如热水器这个例子:烧开,报警,显示温度:
namespace Delegate { class Heater { private int temperature; // 水温 // 烧水 public void BoilWater() { for (int i = 0; i <= 100; i++) { temperature = i; if (temperature > 95) { MakeAlert(temperature); ShowMsg(temperature); } } } // 发出语音警报 private void MakeAlert(int param) { Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:" , param); } // 显示水温 private void ShowMsg(int param) { Console.WriteLine("Display:水快开了,当前温度:{0}度。" , param); } } class Program { static void Main() { Heater ht = new Heater(); ht.BoilWater(); } } }
这种代码就跟我的似的,乱糟糟。根据Observer设计模式,把警报和显示器分离出来,对热水器的温度进行监控:
using System; using System.Collections.Generic; using System.Text; namespace Delegate { // 热水器 public class Heater { private int temperature; public delegate void BoilHandler(int param); //声明委托 public event BoilHandler BoilEvent; //声明事件 // 烧水 public void BoilWater() { for (int i = 0; i <= 100; i++) { temperature = i; if (temperature > 95) { if (BoilEvent != null) { //如果有对象注册 BoilEvent(temperature); //调用所有注册对象的方法 } } } } } // 警报器 public class Alarm { public void MakeAlert(int param) { Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param); } } // 显示器 public class Display { public static void ShowMsg(int param) { //静态方法 Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param); } } class Program { static void Main() { Heater heater = new Heater(); Alarm alarm = new Alarm(); heater.BoilEvent += alarm.MakeAlert; //注册方法 heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法 heater.BoilEvent += Display.ShowMsg; //注册静态方法 heater.BoilWater(); //烧水,会自动调用注册过对象的方法 } } } 输出为: Alarm:嘀嘀嘀,水已经 96 度了: Alarm:嘀嘀嘀,水已经 96 度了: Display:水快烧开了,当前温度:96度。 // 省略...
上面这种不符合.NET的委托和事件命名规范。
.Net Framework的编码规范:
- 委托类型的名称都应该以EventHandler结束。
- 委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
- 事件的命名为 委托去掉 EventHandler之后剩余的部分。
- 继承自EventArgs的类型应该以EventArgs结尾。
再做一下说明:
- 委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
- EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。
上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。
上面的代码按照.NET的规范,可以写为:
using System; using System.Collections.Generic; using System.Text; namespace Delegate { // 热水器 public class Heater { private int temperature; public string type = "RealFire 001"; // 添加型号作为演示 public string area = "China Xian"; // 添加产地作为演示 //声明委托 public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e); 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(BoiledEventArgs e) { if (Boiled != null) { // 如果有对象注册 Boiled(this, e); // 调用所有注册对象的方法 } } // 烧水。 public void BoilWater() { for (int i = 0; i <= 100; i++) { temperature = i; if (temperature > 95) { //建立BoiledEventArgs 对象。 BoiledEventArgs e = 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 Display { public static void ShowMsg(Object sender, Heater.BoiledEventArgs e) { //静态方法 Heater heater = (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(); //烧水,会自动调用注册过对象的方法 } } } 输出为: 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: Display:水快烧开了,当前温度:96度。 // 省略 ...
最后的这段代码我没有进行迁移,明天就考试了,等考完试迁移到工程里试一试。代码什么的,还是亲自敲一敲比较靠谱。
最后就是希望明天考试顺利。还有如果敲代码的话不要再出现莫名其妙的错误了。f*ck。