这是一道非常经典的面试试题.在网上讨论的帖子很多,一些网友给出了十分精彩的解答.这里也只是其中一个比较精简的答案而已.虽然精简,但却通过简单的代码反映了许多人一直不是很清晰的委托,特别是事件的定义和注册问题,也简单涉及了观察者模型的问题.
using System;
namespace CatRatAndHost//猫,老鼠和主人,有趣的委托,事件及观察者模型问题
{
class Program
{
static void Main(string[] args)
{
catclass cat = new catclass();
manclass man = new manclass();
for (int i = 0; i < 5; i++)
{
ratclass rat = new ratclass();//老鼠是观察者,它对猫的动作感兴趣.注意:在这个循环中,new了5只同名的rat.变量rat的作用范围只在{...}内有效.
rat.subject = cat;//设置猫是观察对象,并向猫注册了猫叫事件的事件处理方法--老鼠逃跑.
man.subject = rat;//而老鼠在这里又是观察对象,主人对它的跑感兴趣,所以向老鼠注册它的跑事件的事件处理方法.
}
cat.catEvent += new catclass.catEventHandler(man.wake1);//直接向猫注册事件处理方法.
man.IsWake = false;//主人入睡
cat.catMiao();//猫叫,引起一连串的动作.
Console.ReadKey();
}
}
public interface IObserver //作为观察者,有一个设置观察对象的属性.
{
ISubject subject
{
set;
}
}
public interface ISubject
{}
public class catclass : ISubject //猫对象,继承了观察对象的接口
{
public delegate void catEventHandler(object source, EventArgs e); //申明事件的委托
public event catEventHandler catEvent; //定义事件
public virtual void catMiao() //猫叫,并触发事件
{
Console.WriteLine("The cat: Miao...");
if (catEvent != null)
{
catEvent(this, EventArgs.Empty);
}
}
}
public class ratclass : IObserver,ISubject //老鼠对象,继承观察者接口,同时也是观察对象.
{
public event EventHandler ratEvent; //利用.Net预定义的EventHandler委托直接定义事件.
catclass cat = null; //注册事件处理方法时用到的被观察对象
public ISubject subject //观察对象属性
{
set
{
cat = (catclass)value;
cat.catEvent += new catclass.catEventHandler(run); //注册事件处理方法
}
}
public virtual void run(object source, EventArgs e)//定义事件处理方法
{
Console.WriteLine("The Rat: Run...Run...Run...");
if(ratEvent!=null)
{
ratEvent(this, EventArgs.Empty);
}
}
}
public class manclass : IObserver //主人同老鼠,其实主人也可能被猫叫惊醒.
{
private bool _iswake = true;
public bool IsWake
{
get
{
return _iswake;
}
set
{
_iswake = value;
}
}
ratclass rat = null;
public ISubject subject
{
set
{
rat = (ratclass)value;
rat.ratEvent += new EventHandler(wake2);
}
}
public virtual void wake1(object source, EventArgs e)
{
if (!_iswake) //确保主人不会被重复惊醒
{
Console.WriteLine("The host: Waked by cat...");
_iswake = true;
}
}
public virtual void wake2(object source, EventArgs e)
{
if (!_iswake) //确保主人不会被重复惊醒
{
Console.WriteLine("The host: Waked by rat...");
_iswake = true;
}
}
}
}
代码的用到了事件的两种定义方法:
public event EventHandler ratEvent; //利用.Net预定义的EventHandler委托直接定义事件. 这种方法虽然简单,但对于事件的参数和返回值也都是只能按照预定义好的方法进行.但是,一般情况下,这种方式已足够了.
public delegate void catEventHandler(object source, EventArgs e); //申明事件的委托
public event catEventHandler catEvent; //定义事件,这种方式需要先申明一个相关的委托.然后通过new这个委托来完成事件的注册.相比前面的方式而言要麻烦一些,但是通过这种方式,我们可以自己定义参数列表和返回值,相对较灵活和更强大一些.
其实只能算是一种方法,只是通过不能的途径来完成的而已,最终都是用"观察对象.事件名+=new 事件对应委托(观察者的事件处理方法)"公式来完成的.
一种是采用观察者的属性--观察对象来给观察对象注册事件处理方法.
如:rat.subject = cat;
别一种是申明完观察者后和观察对象,直接注册.
如:cat.catEvent += new catclass.catEventHandler(man.wake1);
※※※※※※※※※※※※※※--我的程序人生--※※※※※※※※※※※※※※