using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace 新特性
{
public delegate void GreetingDelegate(string name);
/// <summary>
/// 委托和事件,委托和事件对observer设计模式的意义
/// </summary>
/// 创建者:qingli.qi
/// 创建日期:2012/7/6
public class 委托和事件
{
/*1.将方法作为方法的参数*/
#region ==1.将方法作为方法的参数 ==
public static void EnglishGreeting(string name)
{
Console.WriteLine("Morning, " + name);
}
/*扩展各国语言,需要新的方法,同时需要更改GreetPeople方法为2,新增枚举类Language*/
public static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
/*如果以后添加国家,需要反复修改GreetPeople和枚举Language,
* 可以直接传每个国家的方法就好了,不需要枚举值和改动GreetPeople*/
/*委托就在这个时候,应运而生,大展生手!*/
/*委托:委托可以定义所需方法的参数类型,本例中的MakeGreeting,修改GreetPeople3*/
public delegate void GreetingDelegate(string name);
public void GreetPeople1(string name)
{
//可以在这里做些额外的事情,比如初始化之类
EnglishGreeting(name);
}
public void GreetPeople2(string name, Language lang)
{
//可以在这里做些额外的事情,比如初始化之类
switch (lang)
{
case Language.English:
EnglishGreeting(name);
break;
case Language.Chinese:
ChineseGreeting(name);
break;
}
}
public static void GreetPeople3(string name, GreetingDelegate MakeGreeting)
{
//可以在这里做些额外的事情,比如初始化之类
MakeGreeting(name);
}
/*委托也是一个类型,在编译时期会编译成类。delegate关键字*/
/*总结一下:委托是一个类,它定义了方法的类型,使得可以将方法当做另一个方法的参数来进行传递,
* 这种将方法动态的赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,
* 同时使得程序具有更好的可扩展性*/
#endregion
/*2.将方法绑定到委托*/
#region ==2.将方法绑定到委托 ==
public static void Test1()
{
GreetingDelegate delegate1, delegate2;
delegate1 = EnglishGreeting;
delegate2 = ChineseGreeting;
GreetPeople3("Jane", delegate1);
GreetPeople3("幽兰", delegate2);
Console.WriteLine("-------------------------------");
}
public static void Test2()
{
GreetingDelegate delegate1;
delegate1 = EnglishGreeting;
delegate1 += ChineseGreeting;
GreetPeople3("Jane", delegate1);
Console.WriteLine("-------------------------------");
}
//绕过GreetPeople
public static void Test3()
{
GreetingDelegate delegate1;
delegate1 = EnglishGreeting;
delegate1 += ChineseGreeting;
delegate1("Jane");
Console.WriteLine("-------------------------------");
}
public static void Test4()
{
GreetingDelegate delegate1;
delegate1 = EnglishGreeting;
delegate1 += ChineseGreeting;
GreetPeople3("Jane", delegate1);
Console.WriteLine("------");
delegate1 -= EnglishGreeting;
GreetPeople3("Jane", delegate1);
Console.WriteLine("-------------------------------");
}
/* 总结二:使用委托可以将多个方法绑定到同一个委托变量,当调用次变量时(这里用“调用”这个词,是因为次变量代表一个方法),可以依次调用所有绑定的方法。*/
#endregion
/*3.事件的由来*/
#region == 事件的由来 ==
/*面向对象讲究的是对象的封装,故将变量封装到GreetManager中*/
public static void test5()
{
GreetManager gm = new GreetManager();
gm.delegate1 = EnglishGreeting;
gm.delegate1 += ChineseGreeting;
gm.GreetPeople("幽兰", gm.delegate1);
Console.WriteLine("-------------------------------");
}
//因为传递参数为gm.delegate1,依然很怪异,修改GreetManager的GreetPeople2
public static void test6()
{
GreetManager gm = new GreetManager();
gm.delegate1 = EnglishGreeting;
gm.delegate1 += ChineseGreeting;
gm.GreetPeople2("幽兰");
Console.WriteLine("-------------------------------");
}
/*总结3:以上委托存在的问题。
* 1.把delegate1声明为private,这简直就是在搞笑。
* 因为声明委托的目的就是为了把它暴露在客户端进行方法注册,
* 你把它声明为private了,客户端对它根本就不可见,那它还有什么用呢?
*
* 2.不管是赋值还是注册,都是讲方法绑定到委托上,除了调用时先后顺序不同,
* 再没有任何的分别,这样也很。。。
*/
/*结合总结3的问题,Event华丽的出场了,他封装了委托类型的变量,使得:在类的内部
* ,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”
* 和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
*
* 改写GreetManager为GreetManager2
*/
/*事件没有什么不好理解,声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已*/
public static void test7()
{
GreetManager2 gm2 = new GreetManager2();
// gm2.MakeGreet = EnglishGreeting;
/*报错,事件“新特性.GreetManager2.MakeGreet”只能出现在 += 或 -= 的左边
* (从类型“新特性.GreetManager2”中使用时除外)*/
gm2.MakeGreet += EnglishGreeting;
gm2.MakeGreet += ChineseGreeting;
gm2.GreetPeople("Jane");
}
#endregion
/*4.事件和委托的编译代码*/
/*
* MakeGreet事件确实是一个GreetingDelegate类型的委托,只不过不管是不是声明为public
* ,它总是被编译为private。另外,它还有两个方法,分别是add_MakeGreet和remove_MakeGreet,
* 这两个方法分别用于注册委托类型的方法和取消注册
*/
/*5.委托和事件与observer设计模式*/
#region ==5.委托和事件与observer设计模式 ==
/*
* 以下例子模拟热水器:通电后,水温超过95度的时候,1.扬声器会开始发出语音,告诉你水的温度
* 2.液晶屏也会改变水温的显示,来提示水已经快烧开了
*/
public static void Heater1()
{
Heater h = new Heater();
h.BoilWater();
Console.WriteLine("-------------------------------");
}
/*heater1 虽然完成了功能却不完美,如果报警器和显示器要分别组装呢。。他们之间如何关联呢。。*/
/*observer设计模式就相当完美了!
*
* observer设计模式主要包括如下两类对象:
* Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本例中,热水器就是一个监视对象
* 它包含的其他感兴趣的内容,就是temperature字段,当这个字段值快到100的时候,会不断把数据发
* 给监视它的对象。
*
*
* Observer:监视着,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会
* 采取相应的行动。在本例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。
*
* 本例中,事情发生的顺序应该是这样的:
* 1.警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)
* 2.热水器知道后保留对警报器和显示器的引用
* 3.热水器进行少数这一动作,当水温超过95度的时候,通过对警报器和显示器的引用,
* 自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。
*
* 类似的例子茫茫多,GOF对它进行了抽象,成为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖
* 关系,以便于当一个对象的装填改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种送耦合的设计模式。
*/
public static void HeaterObserver()
{
Heater2 h2 = new Heater2();
Alarm alarm = new Alarm();
h2.BoilEvent += alarm.MakeAlert;//注册方法
h2.BoilEvent += (new Alarm()).MakeAlert;//给匿名对象注册方法
h2.BoilEvent += Display.ShowMsg;//注册静态方法
h2.BoilWater();//烧水,会自动调用注册过对象的方法
Console.WriteLine("-------------------------------");
}
#endregion
/*6.Net FrameWork中的委托与事件*/
#region ==6.Net FrameWork中的委托与事件 ===
/*
* .NET FrameWork 的编码规范:
* 委托类型的名称都应该以EventHandler结束。
* 委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object类型,一个EventArgs(或继承自EventArgs)
* 事件的命名为 委托去掉EventHandler之后剩余的部分。
* 继承自EventArgs的类型应该以EventArgs结尾
*
* PS:
* 1.委托声明原型找那个的Object类型的参数代表了Subject,也就是监视对象,在本例中是Heater(热水器)。
* 回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)
* 2.EventArgs对象包含了Observer所感兴趣的数据,本例中是temperature
*
*以上这些不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不仅想获得热水器的
*温度,还想在Observer段(警报器或显示器)方法中获得它的生产日期、型号。价格,那么委托和方法的声明
*都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了
*
* 例子Heater3
*
*/
public static void HeaterNetFramework()
{
Heater3 h3 = new Heater3();
Alarm2 a2 = new Alarm2();
h3.Boiled += a2.MakeAlert;
h3.Boiled += (new Alarm2()).MakeAlert;
h3.Boiled += new Heater3.BoiledEventHandler(a2.MakeAlert);
h3.Boiled += Display2.ShowMsg;
h3.BoilWater();
}
#endregion
#region == 7.他家之言==
/*
* 1..NET事件运行原理,事件其实就是委托的封装,封装委托用event关键字,所谓事件
* 就是对整个对象的监控,当改变对象某个属性的时候判断,事件是否注册了方法。如果
* 注册了方法,则调用方法执行。net中,声明委托一般用两个参数:
* public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
* sender为被监控的对象,e为被监控的信息
*
*
* 2.C#中事件用于对象将关于程序中发生的事件(比如用户点击按钮)或已发生的信息的变化通知
* 其他对象。也就是说对象之间使用时间来彼此通信,而不必直接调用对方的方法。这样可以使得
* 对象之间进一步解耦合,便于维护程序,同时也和现实时间更加接近。
*
* 在事件中会涉及到两个对象:
* 1)可以感知事件的发生,并把事件信息通知其他对象的对象成为发布方
* 2)关心发生的事件按,一旦特定事件发生就要进行处理的对象成为订阅方
*
* 事件编程的三个步骤:
* 1)定义事件,因为只有发布方能感知事件的发生,所以一般由发布方定义事件
* 2)预定该事件,订阅方在发布方定义的事件上注册
* 3)通知事件,一旦事件发生,发布方要通知到订阅方
*
* 如何通知?就要使用委托。也就是说所谓定义事件就是要定义一个委托,注册事件就是订阅方让
* 这个委托指向自己关于这个事件的处理程序,
* 而通知事件则是发布方通过调用委使得的事件处理程序可以被执行。
*
*/
#endregion
}
public class GreetManager
{
//在GreetManager的内部声明delegate1变量
public GreetingDelegate delegate1;
public void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
public void GreetPeople2(string name)
{
if (delegate1 != null)
{
delegate1(name);
}
}
}
public class GreetManager2
{
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name)
{
MakeGreet(name);
}
}
public class Heater
{
private int temperature;//温度
//烧水
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature > 95)
{
MakeAlert(temperature);
ShowMsg(temperature);
}
}
}
//语音提示
public void MakeAlert(int param)
{
Console.WriteLine("Alarm:滴滴滴,水已经{0}度了!", param);
}
//显示水温
public void ShowMsg(int param)
{
if (param != 100)
{
Console.WriteLine("Display:水快开了,当前温度{0}度!", param);
}
else
{
Console.WriteLine("Display:水开了,当前温度{0}度!", param);
}
}
}
public class Heater2
{
private int temperature;
public delegate void BoilHander(int param);//声明委托
public event BoilHander 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)
{
if (param != 100)
{
Console.WriteLine("Display:水快开了,当前温度{0}度!", param);
}
else
{
Console.WriteLine("Display:水开了,当前温度{0}度!", param);
}
}
}
public class Heater3
{
private int temperature;
public string Type = "先锋1号";
public string Area = "Made in China";
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;
}
}
//可以供继承自Header3的类重写,以便继承类拒绝其他对象对它的监视
protected virtual void OnBoiled(BoiledEventArgs e)
{
if (Boiled != null)//如果有注册对象
{
Boiled(this, e);//调用Onboiled方法
}
}
public void BoilWater()
{
for (int i = 0; i <= 100; i++)
{
temperature = i;
if (temperature > 95)
{
//建立BoiledEventArgs对象
BoiledEventArgs e = new BoiledEventArgs(temperature);
OnBoiled(e);//调用OnBoiled
}
}
}
}
public class Alarm2
{
public void MakeAlert(object sender, Heater3.BoiledEventArgs e)
{
Heater3 h = sender as Heater3;
Console.WriteLine("Alarm: {0} - {1} :",h.Area,h.Type);
Console.WriteLine("Alarm:滴滴滴,水已经{0}度了!" ,e.temperature);
Console.WriteLine();
}
}
public class Display2
{
public static void ShowMsg(object sender, Heater3.BoiledEventArgs e)
{
Heater3 h = sender as Heater3;
Console.WriteLine("Display: {0} - {1} :", h.Area, h.Type);
if (e.temperature != 100)
{
Console.WriteLine("Display:水快开了,当前温度{0}度!", e.temperature);
}
else
{
Console.WriteLine("Display:水开了,当前温度{0}度!", e.temperature);
}
Console.WriteLine();
}
}
public enum Language
{
English,
Chinese,
}
}