C#基础-第11章:事件

    * 11.1 本章内容:
        · 设计公开事件的类型
        · 编译器如何实现事件
        · 设计侦听事件的类型
        · 显式实现事件
/* ==============================================================================

 ==============================================================================
 */
//#define CompilerImplementedEventMethods
using System;
using System.Threading;

public static class Events
{
    public static void Main()
    {
        
        MailManager.Go();

        TypeWithLotsOfEventsTest();
    }

    private static void TypeWithLotsOfEventsTest()
    {
        // 测试事件代码
        TypeWithLotsOfEvents twle = new TypeWithLotsOfEvents();

        // 添加一个回调
        twle.Foo += HandleFooEvent;
        twle.SimulateFoo();
        Console.WriteLine("The callback was invoked 1 time above" + Environment.NewLine);

        // 再次 添加一个回调,则出现2次调用
        twle.Foo += HandleFooEvent;
        twle.SimulateFoo();
        Console.WriteLine("The callback was invoked 2 times above" + Environment.NewLine);

        // 移除一个回调
        twle.Foo -= HandleFooEvent;
        twle.SimulateFoo();
        Console.WriteLine("The callback was invoked 1 time above" + Environment.NewLine);

        // 移除一个回调
        twle.Foo -= HandleFooEvent;
        twle.SimulateFoo();
        Console.WriteLine("The callback was invoked 0 times above" + Environment.NewLine);

        Console.WriteLine("Press <Enter> to terminate this application.");
        Console.ReadLine();
    }

    private static void HandleFooEvent(object sender, FooEventArgs e)
    {
        Console.WriteLine("Handling Foo Event here...");
    }
}

///////////////////////////////////////////////////////////////////////////////

// 第1步 #1: 定义一个类型来容纳所有应该发送事件通知接收者的附加信息 
internal sealed class NewMailEventArgs : EventArgs //P223
{

    private readonly String m_from, m_to, m_subject;

    public NewMailEventArgs(String from, String to, String subject)
    {
        m_from = from; m_to = to; m_subject = subject;
    }

    public String From { get { return m_from; } }
    public String To { get { return m_to; } }
    public String Subject { get { return m_subject; } }
}

internal class MailManager
{
    public static void Go()
    { 
        // 创建一个MailManager 对象
        MailManager mm = new MailManager();

        Fax fax = new Fax(mm);
        Pager pager = new Pager(mm);

        //这里会显示2条 通知 Fax,Pag
        mm.SimulateNewMail("Jeffrey", "Kristin", "I Love You!");

        fax.Unregister(mm);//取消关注

        //这里只会显示1条
        mm.SimulateNewMail("Jeffrey", "Mom & Dad", "Happy Birthday.");
    }

#if CompilerImplementedEventMethods
   // 第2步 #2: 定义事件成员
    public event EventHandler<NewMailEventArgs> NewMail;
#else
    //定义事件成员
    private EventHandler<NewMailEventArgs> m_NewMail;

    // 填加一个时间成员
    public event EventHandler<NewMailEventArgs> NewMail
    {
        //add remove 方法一线程安全的一种模式更新值
        add
        {
         
            m_NewMail += value;
        }

      
        remove
        {
        
            m_NewMail -= value;
        }
    }

#endif

    // 第3步 #3: 定义负责引发事件的方法来通知已登记的对象
    // 如果类是密封的,该方法要声明为私有和非虚
    protected virtual void OnNewMail(NewMailEventArgs e)
    {
        // Copy a reference to the delegate field now into a temporary field for thread safety 
        //e.Raise(this, ref m_NewMail);

#if CompilerImplementedEventMethods
        //出于线程安全考虑,现在将委托字段的引用复制到一个临时变量中
        EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail);
#else
        //定义个事件委托
        EventHandler<NewMailEventArgs> temp = Volatile.Read(ref m_NewMail);
#endif

        // 任何方法登记了对事件的关注,就通知它们
        if (temp != null)
        {
            temp(this, e);
        }
    }

    // Step #4: 定义方法将输入转化为期望事件 
    public void SimulateNewMail(String from, String to, String subject)
    {

        //构造一个对象来容纳想传给通知接收者的信息
        NewMailEventArgs e = new NewMailEventArgs(from, to, subject);

        //调用虚方法通知对象事件已发生,
        //如果没有类型重写该方法,我们的对象将通知事件的所有登记对象
        OnNewMail(e);
    }
}

public static class EventArgExtensions
{
    public static void Raise<TEventArgs>(this TEventArgs e, Object sender, ref EventHandler<TEventArgs> eventDelegate)
    {
        // Copy a reference to the delegate field now into a temporary field for thread safety 
        EventHandler<TEventArgs> temp = Volatile.Read(ref eventDelegate);

        // If any methods registered interest with our event, notify them  
        if (temp != null) temp(sender, e);
    }
}

//设计侦听事件的类型
internal sealed class Fax //P228
{
    // 将 MailManager 对象传给构造器
    public Fax(MailManager mm)
    {
        //构件EventHandler<NewMailEventArgs>委托的的一个实例
        //使它应用我们的FaxMsg 回调方法
        // 向MailManager的NewMail事件等我们的回调方法
        mm.NewMail += FaxMsg;
    }

    // 新邮件到达时,MailManager将调用这个方法
    private void FaxMsg(Object sender, NewMailEventArgs e)
    {
        // 'sender' 表示MailManager的对象,便于将信息传回给它

        // 'e' 表示MailManager对象想传给我的附加事件信息
        // 这里的代码正常情况下应该传真电子邮件
        // 但这个测试性的实现只是在控制台上显示邮件

        Console.WriteLine("Faxing mail message:");
        Console.WriteLine("   From={0}, To={1}, Subject={2}",
           e.From, e.To, e.Subject);
    }

    //执行这个方法,Fax对象将向NewMail事件注销自己对它的关注。
    //以后不再接收通知
    public void Unregister(MailManager mm)
    {
      //向MailManager的NewMail事件注销自己对这个事件的关注
        mm.NewMail -= FaxMsg;
    }
}

///////////////////////////////////////////////////////////////////////////////

internal sealed class Pager
{
    public Pager(MailManager mm)
    {
        mm.NewMail += SendMsgToPager;
    }
    private void SendMsgToPager(Object sender, NewMailEventArgs e)
    {
        Console.WriteLine("Sending mail message to pager:");
        Console.WriteLine("   From={0}, To={1}, Subject={2}", e.From, e.To, e.Subject);
    }
    public void Unregister(MailManager mm)
    {
        mm.NewMail -= SendMsgToPager;
    }
}

//////////////////////////////// End of File //////////////////////////////////

2.

//////////////////////////////// 显示实现事件 ////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Threading;


///////////////////////////////////////////////////////////////////////////////



//这个类的目的是在使用EventSet 多一点的类型安全性和代码可维护性
public sealed class EventKey : Object //P230
{

}


///////////////////////////////////////////////////////////////////////////////


public sealed class EventSet
{
   //该私有字典用于维护EventKey -> Delegate 映射

    private readonly Dictionary<EventKey, Delegate> m_events =
        new Dictionary<EventKey, Delegate>();

    //添加EventKey -->Delegate 映射(如果EventKey 不存在),
    // 或者将委托和现有的EventKey合并
    public void Add(EventKey eventKey, Delegate handler)
    {
        Monitor.Enter(m_events);
        Delegate d;
        m_events.TryGetValue(eventKey, out d);
        m_events[eventKey] = Delegate.Combine(d, handler);
        Monitor.Exit(m_events);
    }

    // 从EventKey (如果它存在) 删除委托,并且 
    // 在删除最后一个委托时删除EventKey --> Delegate映射
    public void Remove(EventKey eventKey, Delegate handler)
    {
        Monitor.Enter(m_events);
       //调用TryGetValue,确保在尝试从集合中删除不存在的EventKey时不会抛出异常
        Delegate d;
        if (m_events.TryGetValue(eventKey, out d))
        {
            d = Delegate.Remove(d, handler);

            //如果还有委托,就设置新的头部(地址),否则删除EventKey
            if (d != null) m_events[eventKey] = d;
            else m_events.Remove(eventKey);
        }
        Monitor.Exit(m_events);
    }

    // 为指定的EventKey 引发事件
    public void Raise(EventKey eventKey, Object sender, EventArgs e)
    {
        //  如果EventKey不在集合,不抛出异常
        Delegate d;
        Monitor.Enter(m_events);
        m_events.TryGetValue(eventKey, out d);
        Monitor.Exit(m_events);

        if (d != null)
        {
            //由于字典可能包含几个不同的委托类型
            //所以无法在编译时构造一个类型安全的委托调用
            //因此,我调用System.Deleagte类型的DynamicInvoe
            //方法,以一个对象数组的形式向它传递回调方法的参数
            //在内部,DynamicInvoke会向调用的回调方法查证参数的
            //类型安全性,并调用方法
            //如果存在类型不匹配的情况,DynamicInvce 会抛出异常
            d.DynamicInvoke(new Object[] { sender, e });
        }
    }
}


//////////////////////////////// End of File //////////////////////////////////

3.

using System;

///////////////////////////////////////////////////////////////////////////////

//这个事件定义从EventArgs派生的类型
public class FooEventArgs : EventArgs { }

// 这个事件定义从EventArgs派生的类型
public class BarEventArgs : EventArgs { }

///////////////////////////////////////////////////////////////////////////////

internal class TypeWithLotsOfEvents //P232
{
    // 定义私有实例字段来引用集合
    // 集合用于管理一组“事件/委托”对
    // 注意 EventSet 类型不是FCL的一部分,它是我自己的类型
    private readonly EventSet m_eventSet = new EventSet();

    //受保护的属性使派生类能访问集合
    protected EventSet EventSet { get { return m_eventSet; } }

    #region 用于支持Foo事件的代码(为附加的事件重复这个模式)
    //定义Foo 事件必要的成员
    //2a.构造一个静态只读对象来标识这个事件
    // 每个对象都自己的哈希码,以便在对象的集合中查找这个事件的委托链表

    protected static readonly EventKey s_fooEventKey = new EventKey();

    // 2b. 定义事件的访问器方法,用于在集合中增删委托
    public event EventHandler<FooEventArgs> Foo
    {
        add { m_eventSet.Add(s_fooEventKey, value); }
        remove { m_eventSet.Remove(s_fooEventKey, value); }
    }

    // 2c. 为这个事件定义受保护的虚方法OnFoo
    protected virtual void OnFoo(FooEventArgs e)
    {
        m_eventSet.Raise(s_fooEventKey, this, e);
    }

    // 2d. 定义将输入转换成这个事件的方法
    public void SimulateFoo()
    {
        OnFoo(new FooEventArgs());
    }
    #endregion

    #region Code to support the Bar event
    // 3. Define the members necessary for the Bar event.
    // 3a. Construct a static, read-only object to identify this event.
    // Each object has its own hash code for looking up this
    // event抯 delegate linked list in the object抯 collection.
    protected static readonly EventKey s_barEventKey = new EventKey();

    // 3d. Define the event抯 accessor methods that add/remove the
    // delegate from the collection.
    public event EventHandler<BarEventArgs> Bar
    {
        add { m_eventSet.Add(s_barEventKey, value); }
        remove { m_eventSet.Remove(s_barEventKey, value); }
    }

    // 3e. Define the protected, virtual On method for this event.
    protected virtual void OnBar(BarEventArgs e)
    {
        m_eventSet.Raise(s_barEventKey, this, e);
    }

    // 3f. Define the method that translates input to this event.
    public void SimulateBar()
    {
        OnBar(new BarEventArgs());
    }
    #endregion
}

//////////////////////////////// End of File //////////////////////////////////

 

posted @ 2019-01-18 10:14  eric.yuan  阅读(270)  评论(0编辑  收藏  举报