您的位置:首页 > 编程语言 > C#

C# 中的委托和事件详解(四)

2010-01-11 19:17 405 查看
类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。
实现范例的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 Framework中的委托与事件尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.Net Framework 中的事件模型和上面的不同?为什么有很多的EventArgs参数?
在回答上面的问题之前,我们先搞懂 .Net Framework的编码规范:

委托类型的名称都应该以EventHandler结束。
委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
事件的命名为 委托去掉 EventHandler之后剩余的部分。
继承自EventArgs的类型应该以EventArgs结尾。
再做一下说明:

委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是 Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
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 = "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度。
// 省略 ...
总结
在本文中我首先通过一个GreetingPeople的小程序向大家介绍了委托的概念、委托用来做什么,随后又引出了事件,接着对委托与事件所产生的中间代码做了粗略的讲述。
在第二个稍微复杂点的热水器的范例中,我向大家简要介绍了 Observer设计模式,并通过实现这个范例完成了该模式,随后讲述了.Net Framework中委托、事件的实现方式。

例子:

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate
{
/*
* 老鼠出来找食物,正在抢食物时,其中一只老鼠突然发现了一只猫,于是叫了一声:猫来了,大家快跑.
* 老鼠的叫声,引起了猫的注意.猫看到这么多美食,太开心了,于是也叫了一声.
* 猫的叫声,吵醒了熟睡中的主人.于是主人说话了,并过去瞧个究竟.
*/
class Mouse
{
public delegate void MouseDelegate(Object sender, MouseEventArgs e);//声明委托
public event MouseDelegate callEventHandler;//声明事件

private bool seeCat = false;//是否发现猫

public class MouseEventArgs : EventArgs
{
public int mouseNumber = 10;
public MouseEventArgs(int number)
{
this.mouseNumber = number;
}
}

public void Called(Object sender, MouseEventArgs e)
{
Console.WriteLine("老鼠惊叫:我发现猫来了,大家快跑...,于是 {0} 个老鼠一起抱头鼠窜",e.mouseNumber);
Console.WriteLine("---------- 以上是老鼠的动作 -----------");
}
public void FindFood()
{
for(int i=10;i<24;i++)
{
if(i==18)
{
Console.WriteLine("晚上18点,老鼠出来行走了....");
}
if (i == 20)
{
Console.WriteLine("晚上20点,老鼠正找食物....");
}
if (i == 21)
{
Console.WriteLine("晚上21点,老鼠找到食物,开始美美的享受美餐....");
}
if (i == 22)
{
Console.WriteLine("晚上22点,老鼠越来越多,食物不够分,引起轰抢....");
}
if (i == 23)
{
Console.WriteLine("正当老鼠抢得不可开交时,突然有一只老鼠发现了一只猫....");
seeCat = true;
break;
}
}
if (seeCat)
{
MouseEventArgs e = new MouseEventArgs(10);
SeeCat(e);
}
}

public virtual void SeeCat(MouseEventArgs e)
{
if (callEventHandler != null)
{
callEventHandler(this, e);
}
}
}

class Cat
{
public void Hear(Object sender, Mouse.MouseEventArgs e)
{
Console.WriteLine("猫心里想:我听到老鼠的叫声了.嘿嘿,美餐来了!");
Walking(e);
}

private void Walking(Mouse.MouseEventArgs e)
{
Console.WriteLine("猫飞快地朝老鼠发出声音的方向走去...");
See(e);
}

private void See(Mouse.MouseEventArgs e)
{
Console.WriteLine("猫看到老鼠了.");
Called(e);
}

private void Called(Mouse.MouseEventArgs e)
{
Console.WriteLine("猫欣喜若狂地说:哇!这么多只老鼠,我数数...天啊!一共有 {0} 只老鼠...", e.mouseNumber);
Console.WriteLine("---------- 以上是猫的动作 -----------");
}
}

class Human
{
public void Hear(Object sender, Mouse.MouseEventArgs e)
{
Called();
}

private void Called()
{
Console.WriteLine("主人说:这只死猫,还让不让人睡觉!看看去,到底是怎么了?");
Walking();
}

private void Walking()
{
Console.WriteLine("go go go !");
Console.WriteLine("---------- 以上是人的动作 -----------");
}
}

class Program
{
static void Main()
{
Mouse m = new Mouse();
m.callEventHandler += m.Called;
Cat c = new Cat();
m.callEventHandler += c.Hear;
Human man = new Human();
m.callEventHandler += man.Hear;
m.FindFood();
string a = Console.Read().ToString();
}
}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: