也谈事件---Mom,Baby and you[一,二,三,四]

    朋友家有一个宝宝饿的时候总是喜欢哭闹,想尿尿的时候就开始哼哼唧唧或是手舞足蹈。
    如果大人够细致,就能分辨出宝宝的这种哭闹是饿了,这可以叫做饥饿性哭闹。
    同理,这种哼哼唧唧或是手舞足蹈也是在通知大人他想尿尿了。
    上面的这两个过程就是我要说的事件应用场景,咱们慢慢模拟这一场景:
    一、Mom忙碌的一天
    最简单的思路,当孩子哭闹和哼哼唧唧的时候,妈妈要有响应,那么我们可能要定义下面这几项:
    (1)事件委托,名称为:事件名+EventHandler。
    (2)事件成员,名称为前面的委托名去掉EventHandler。
    于是乎我们写出来下面的Baby:

 public class Baby
    {
        
//(1)定义事件委托
        public delegate void CryEventHandler();
        
public delegate void CroonEventHandler();
        
//(2)定义事件成员
        public event CryEventHandler Cry;//饥饿性哭闹
        public event CroonEventHandler Croon;//哼哼唧唧
        public void Action()
        {
            Console.WriteLine(
"CryCry");
            
//调用委托时进行null检查
            if (Cry != null)
            {
                Cry();
            }
            Console.WriteLine(
"CroonCroon");
            
if (Croon != null)
            {
                Croon();
            }
        }
    }

    和下面的Mom:

       
public class Mom
    {
        
public void Action()
        {
            Baby baby 
= new Baby();
            baby.Cry 
+= new Baby.CryEventHandler(baby_Cry);
            baby.Croon 
+= new Baby.CroonEventHandler(baby_Croon);
            baby.Action();
        }
        
public void baby_Cry()
        {
            Console.WriteLine(
"妈妈开始给baby准备食物");
        }
        
public void baby_Croon()
        {
            Console.WriteLine(
"妈妈抱起baby嘘嘘");
        }
    }

 

 

    看看有什么问题,没曾想宝宝还有个小妹妹。
    YES!他们是双胞胎^_^。上面写的东西肯定是不满足了。
    因为我们分辨不出是哪个宝宝在哭闹或想嘘嘘。
    二、你和两个Baby
    前面说了,这个Mom有两个Baby。我们前面的实现太粗糙了。
    假设您现在正在这位Mom家里做客,这个时候Mom正在为您准备一顿大餐。
    照顾小孩的责任一下落在了您的头上。如果您和Mom一家很熟,Mom可能会告诉您
    “小强哭两声就是饿了。小丽哭一声就是饿了。
    小强哼哼两声就是要嘘嘘了,小丽哼哼一声就是要嘘嘘了”
    如果您和Mom一家不是很熟,Mom可能会告诉您
     “男孩哭两声就是饿了。女孩哭一声就是饿了。
    男孩哼哼两声就是要嘘嘘了,女孩哼哼一声就是要嘘嘘了”
    我想,您已经明白Mom的意思了。
    通过参考委托和事件的定义规范我们总结出这么几条:

     (1)事件委托名称应以 EventHandler 结尾。
     (2)事件委托应该有两个参数:
        第一个是 object 类型的 sender,代表发出事件通知的对象。
        第二个参数 e,应该是 EventArgs 或其派生类型。
    (3)事件参数类型,应从 EventArgs 继承,名称应以 EventArgs 结尾。应将所有想通过事件传达到外界的信息都放在事件参数 e 中。
     (4)应该为每个事件提供一个 protected  virtual的 OnXxxx 方法:方法名称为 On 加上事件的名称;只有一个事件参数 e;
        并在该方法中进行 null 判断。 在需要发出事件通知的地方,调用OnXxxx 方法。
     于是有了下面的Baby

    public class Baby2
    {
        
//Cry事件参数
        public class CryEventArgs : EventArgs
        {
            
private string _name = string.Empty;
            
private string _sex = string.Empty;
            
private string _voice = string.Empty; 
            
public string Name
            {
                
get { return _name; }
                
set { _name = value; }
            }
            
public string Sex
            {
                
get { return _sex; }
                
set { _sex = value; }
            }
            
public string Voice
            {
                
get { return _voice; }
                
set { _voice = value; }
            }
            
public CryEventArgs(string name,string sex,string voice)
            {
                _name 
= name;
                _sex 
= sex;
                _voice 
= voice;
            }
        }
        
//Croon事件参数
        public class CroonEventArgs : EventArgs
        {
            
private string _name = string.Empty;
            
private string _sex = string.Empty;
            
private string _hmm = string.Empty;
            
public string Name
            {
                
get { return _name; }
                
set { _name = value; }
            }
            
public string Sex
            {
                
get { return _sex; }
                
set { _sex = value; }
            }
            
public string Hmm
            {
                
get { return _hmm; }
                
set { _hmm = value; }
            }
            
public CroonEventArgs(string name, string sex, string hmm)
            {
                _name 
= name;
                _sex 
= sex;
                _hmm 
= hmm;
            }
        }
        
//定义事件委托
        public delegate void CryEventHandler(object sender, CryEventArgs e);
        
public delegate void CroonEventHandler(object sender, CroonEventArgs e);
        
//定义事件成员
        public event CryEventHandler Cry;
        
public event CroonEventHandler Croon;
        
//受保护虚方法
        protected virtual void OnCry(CryEventArgs e)
        {
            
if (Cry != null)
            {
                Cry(
this, e);
            }
        }
        
protected virtual void OnCroon(CroonEventArgs e)
        {
            
if (Croon != null)
            {
                Croon(
this, e);
            }
        }
        
public void Action()
        {
            Console.WriteLine(
"CryCry");
            OnCry(
new CryEventArgs("小强","男孩","哭了两声"));
            Console.WriteLine(
"CroonCroon");
            OnCroon(
new CroonEventArgs("小强""男孩""哼哼了两声"));
        }
    }

 和下面的您:

 
public class You
    {
        
public void Action()
        {
            Baby2 baby 
= new Baby2();
            baby.Cry 
+= new Baby2.CryEventHandler(baby_Cry);
            baby.Croon 
+= new Baby2.CroonEventHandler(baby_Croon);
            baby.Action();
        }
        
private void baby_Cry(object sender, Baby2.CryEventArgs e)
        {
            Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + e.Name +e.Voice);
            Console.WriteLine(
"您开始给baby准备食物");
            Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + e.Sex + e.Voice);
            Console.WriteLine(
"您开始给baby准备食物");
        }
        
private void baby_Croon(object sender, Baby2.CroonEventArgs e)
        {
            Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + e.Name +e.Hmm);
            Console.WriteLine(
"您抱起baby嘘嘘");
            Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + e.Sex + e.Hmm);
            Console.WriteLine(
"您抱起baby嘘嘘");
        }
    }

故事到这里还没有结束,因为还会有下面的这种情况出现:
 三、Baby的无奈
    前面事件声明时,无论是否有事件处理程序挂接,它都会占用一定的内存空间。
    也就是说,小强哭闹以后,您给他吃东西了。他吃完东西又要嘘嘘了。
    但这个时候您也去嘘嘘了,您不在小强的身边,所以小强要嘘嘘的动作没有人来响应。
    使用下面的方法。在Baby类中将不创建Croon事件,就是说不让小强哼哼了,因为哼哼了也没用^_^
    所以我们提供类似属性的事件声明,事件声明使用add,remove访问器:
       public event [委托类型] [事件名称]
       {
          add { .... }
          remove { .... }
       }
   修改Baby类如下:

 
 public class Baby3
    {
        
//Cry事件参数
        public class CryEventArgs : EventArgs
        {
            
private string _name = string.Empty;
            
private string _sex = string.Empty;
            
private string _voice = string.Empty;
            
public string Name
            {
                
get { return _name; }
                
set { _name = value; }
            }
            
public string Sex
            {
                
get { return _sex; }
                
set { _sex = value; }
            }
            
public string Voice
            {
                
get { return _voice; }
                
set { _voice = value; }
            }
            
public CryEventArgs(string name, string sex, string voice)
            {
                _name 
= name;
                _sex 
= sex;
                _voice 
= voice;
            }
        }
        
//Croon事件参数
        public class CroonEventArgs : EventArgs
        {
            
private string _name = string.Empty;
            
private string _sex = string.Empty;
            
private string _hmm = string.Empty;
            
public string Name
            {
                
get { return _name; }
                
set { _name = value; }
            }
            
public string Sex
            {
                
get { return _sex; }
                
set { _sex = value; }
            }
            
public string Hmm
            {
                
get { return _hmm; }
                
set { _hmm = value; }
            }
            
public CroonEventArgs(string name, string sex, string hmm)
            {
                _name 
= name;
                _sex 
= sex;
                _hmm 
= hmm;
            }
        }
        
/// <summary>
        
/// 定义事件委托
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        public delegate void CryEventHandler(object sender, CryEventArgs e);
        
public delegate void CroonEventHandler(object sender, CroonEventArgs e);
        
// 为每种事件生成一个唯一的 object 作为键
        static readonly object CryEventKey = new object();
        
static readonly object CroonEventKey = new object();
        
//存储事件处理程序
        protected Dictionary<object, Delegate> dict = new Dictionary<object, Delegate>();
        
//private Hashtable dict = new Hashtable();

        
//向dict中添加事件
        protected void AddEventHandler(object eventKey, Delegate handler)
        {
            
lock (this)
            {
                
if (!dict.ContainsKey(eventKey))
                {
                    dict.Add(eventKey, handler);
                }
                
else
                {
                    dict[eventKey] 
= handler;
                }
            }
        }
        
//从dict中移除事件
        protected void RemoveEventHandler(object eventKey)
        {
            
lock (this)
            {
                dict.Remove(eventKey);
            }
        }
        
//从dict中获得事件
        protected Delegate GetEventHandler(object eventKey)
        {
            
if (!dict.ContainsKey(eventKey))
                
return null;
            
else
                
return (Delegate)dict[eventKey];
        }
        
//定义事件成员
        public event CryEventHandler Cry
        {
            add { AddEventHandler(CryEventKey, value); }
            remove { RemoveEventHandler(CryEventKey); }
        }
        
public event CroonEventHandler Croon
        {
            add { AddEventHandler(CroonEventKey, value); }
            remove { RemoveEventHandler(CroonEventKey); }
        }
        
//受保护虚方法
        protected virtual void OnCry(CryEventArgs e)
        {
            CryEventHandler Cry 
= (CryEventHandler)GetEventHandler(CryEventKey);
            
if (Cry != null)
            {
                Cry(
this, e);
            }
        }
        
protected virtual void OnCroon(CroonEventArgs e)
        {
            CroonEventHandler Croon 
= (CroonEventHandler)GetEventHandler(CroonEventKey);
            
if (Croon != null)
            {
                Croon(
this, e);
            }
        }
        
public void Action()
        {
            Console.WriteLine(
"CryCry");
            OnCry(
new CryEventArgs("小强""男孩""哭了两声"));
            Console.WriteLine(
"CroonCroon");
            OnCroon(
new CroonEventArgs("小强""男孩""哼哼了两声"));
        }
    }

实际上,我们只是用一个Dictionary 存储外部挂接上的事件处理程序。通过add和remove操作实现了事件的增加和删除。
简单的修改一下您,因为Baby Croon的时候您去嘘嘘了:

 public class You2
    {
        
public void Action()
        {
            Baby3 baby 
= new Baby3();
            baby.Cry 
+= new Baby3.CryEventHandler(baby_Cry);
            
//baby.Croon += new Baby3.CroonEventHandler(baby_Croon);
            baby.Action();
        }
        
private void baby_Cry(object sender, Baby3.CryEventArgs e)
        {
            Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + e.Name + e.Voice);
            Console.WriteLine(
"您开始给baby准备食物");
            Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + e.Sex + e.Voice);
            Console.WriteLine(
"您开始给baby准备食物");
        }
        
private void baby_Croon(object sender, Baby3.CroonEventArgs e)
        {
            Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + e.Name + e.Hmm);
            Console.WriteLine(
"您抱起baby嘘嘘");
            Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + e.Sex + e.Hmm);
            Console.WriteLine(
"您抱起baby嘘嘘");
        }
    }

四、接口实现
    再补充一个接口实现,思路是引入一个接口实现回调。

下面是Baby及接口实现:

 public interface IBaby
    {
        
void Cry(string name,string sex,string voice);
        
void Croon(string name,string sex,string hmm);
        
void Other(string name, string sex, string active);
    }
    
public class BabyAdapter : IBaby
    {
        
public virtual void Cry(string name, string sex, string voice) { }
        
public virtual void Croon(string name, string sex, string hmm) { }
        
public virtual void Other(string name, string sex, string active) { }
    }
    
public class Baby4
    {
        
private BabyAdapter b = null;
        
public Baby4(BabyAdapter b)
        {
            
this.b = b;
        }
        
public void Action()
        {
            Console.WriteLine(
"CryCry");
            b.Cry(
"小强""男孩""哭了两声");
            Console.WriteLine(
"CroonCroon");
            b.Croon(
"小强""男孩""哼哼了两声");
        }
    }
上面代码Baby继承自adapter类,由外界传入adapter实例实现回调。
之所以使用adapter类而不是直接继承自接口是因为外界不必实现接口中的所有方法。
下面就是YOU:
 public class You3
    {
        
private class MyBaby:BabyAdapter
        {
            
public override void Cry(string name,string sex,string voice)
            {
                Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + name + voice);
                Console.WriteLine(
"您开始给baby准备食物");
                Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + sex + voice);
                Console.WriteLine(
"您开始给baby准备食物");
            }
            
public override void Croon(string name, string sex, string hmm)
            {
                Console.WriteLine(
"如果您是Mom的熟人,你会这样分析:" + name + hmm);
                Console.WriteLine(
"您抱起baby嘘嘘");
                Console.WriteLine(
"如果您不是Mom的熟人,你会这样分析:" + sex + hmm);
                Console.WriteLine(
"您抱起baby嘘嘘");
            }
        }
        
public void Action()
        {
            Baby4 baby 
= new Baby4(new MyBaby());
            baby.Action();
        }
    }
看一下结果:

posted @ 2009-08-04 08:36  青羽  阅读(3688)  评论(12编辑  收藏  举报