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 //////////////////////////////////