主要内容转自http://www.tracefact.net/csharp-programming/delegates-and-events-in-csharp.aspx
根据自己理解进行了整理
----------------------------------
委托
----------------------------------
委托,类似于c中的函数指针,在C#中是一种类型,和string类型一样。(委托实质是一个类)委托可以使用=,=+,=-,new运算符,将多个函数赋值到委托。委托可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。代码如下:
private static void EnglishGreeting(string name) {
Console.WriteLine("Morning, " + name);
}
private static void ChineseGreeting(string name) {
Console.WriteLine("早上好, " + name);
}
delegate void GreetingDelegate(string name);//定义一个委托类型
GreetingDelegate delegate1;// 声明一个委托类型变量
delegate1 = EnglishGreeting;// 先给委托类型的变量赋值
delegate1 += ChineseGreeting;// 给此委托变量再绑定一个方法
// 调用委托
delegate1 ("Jimmy Zhang");// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。使用该委托时,绑定的函数会依次执行。上面代码可以简化为:
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
----------------------------------
事件
----------------------------------
委托作为一个类型,在一个类中可能会有一个委托类型成员对象。将该委托类型成员对象设置public就严重破坏对象的封装性。但如果设置为private就无法为该委托类型成员对象赋值。因此,使用C#的属性对委托类型成员对象进行封装,就可解决该问题。为了简化属性封装,产生了event关键字。代码如下:
public class GreetingManager{
// 声明一个事件(属性封装委托)
public event GreetingDelegate MakeGreet;
public void GreetPeople(string name) {
MakeGreet(name);
}
}
----------------------------------
Observer设计模式(观察者模式)
----------------------------------
观察者模式包含两个对象,一个是subject(被观察对象),一个是Observer(观察者对象)。
定义:Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。
示例代码:
// 热水器
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(); //烧水,会自动调用注册过对象的方法
}
}
说明:Heater类有BoilEvent对象(public),对外提供BoilEvent事件通知。需要获得BoilEvent事件,只需要注册即可。
----------------------------------
.Net Framework中的委托与事件
----------------------------------
.Net Framework的编码规范:
1、委托类型的名称都应该以EventHandler结束
2、委托的原型定义有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)
其中这个Object类型就是观察者模式中的subject(被观察对象),其作用是为了Observer(观察者对象)注册的方法可以访问subject(被观察对象)的public成员。上面代码例子中,subject(被观察对象)就是heater,Observer(观察者对象)注册的方法就是alarm.MakeAlert或者Display.ShowMsg
其中EventArgs对象包含了Observer所感兴趣的数据,上面代码例子中就是MakeAlert(int param)函数中的param,也就是heater中的temperature
3、事件的命名为 委托去掉 EventHandler之后剩余的部分
4、继承自EventArgs的类型应该以EventArgs结尾
按照上述规范,改写上部分代码
// 热水器
public class Heater
{
private int temperature;
public string type = "RealFire 001"; // 添加型号作为Object sender访问演示
public string area = "China Xian"; // 添加产地作为Object sender访问演示
public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);//定义委托
public event BoiledEventHandler Boiled; //声明事件
// 定义BoiledEventArgs类,作为EventArgs访问演示
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(); //烧水,会自动调用注册过对象的方法
}
}