幸运星空

Lucker的程序人生

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

这是一道非常经典的面试试题.在网上讨论的帖子很多,一些网友给出了十分精彩的解答.这里也只是其中一个比较精简的答案而已.虽然精简,但却通过简单的代码反映了许多人一直不是很清晰的委托,特别是事件的定义和注册问题,也简单涉及了观察者模型的问题.

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);

posted on 2009-03-18 15:36  Lucker  阅读(559)  评论(0编辑  收藏  举报