《Effective C#》读书笔记——条目25:用事件模式实现通知<使用C#表达设计>
2013-01-18 23:59
1081 查看
.NET中的事件其实就是一个观察者模式(Observer Pattern)的一个语法上的快捷实现(更多可以参考:使用委托和事件实现观察者模式)。事件是一种内建的委托,用来为事件处理函数提供类型安全的方法签名。事件就是对象将信息告知观察者的方式。
然后是日志类本身:
在这里AddMsg()是触发事件的方法,LoggerEventArgs类中定义了事件的优先级和消息内容,委托则为事件处理函数定义了签名。在Logger类内部,事件自动Log定义了事件处理函数。编译器看到这个字段后会自动创建对应的Add和Remove操作符,编译器生成的代码和下面类似:
或者我们可以直接查看IL代码:
View Code
事件提供了一种标准的机制来通知侦听者。.NET的事件模式使用了事件语法来实现观察者模式。任意数量的客户对象都可以将自己的处理函数注册到事件上,然后处理这些事件。这些客户对象不需要在编译器就给出,事件也不必非有订阅者才能正常工作。在C#中使用事件可以降低发送者和可能的通知接收者之间的耦合。发送者完全可以独立于接收者进行开发。事件是实现广播类型行为的标准方式。
1.发布者定义事件
我们来看一个例子,有一个日志类,将应用程序需要分发的信息发送个各个侦听者,这些侦听者可以是控制域、系统日志、数据库等等,首先定义一个在事件触发中负责传递消息的事件参数类:public class LoggerEventArgs : EventArgs { public string message { get; private set; } public int Priority { get; private set; } public LoggerEventArgs(int p, string m) { this.Priority = p; this.message = m; } }
然后是日志类本身:
public class Logger { static Logger() { theOnly = new Logger(); } private Logger() { } private static Logger theOnly = null; public static Logger Singleton { get { return theOnly; } } //定义事件 public event EventHandler<LoggerEventArgs> Log; //在这里增加消息和日志 public void AddMsg(int priority, string msg) { //这里引用临时变量是一个重要的安全措施,可预防多线程环境中的竞争条件 //若是没有引用的副本,客户代码可能会在if判断语句和事件处理函数之间移 //除事件处理函数,而复制引用之后即可避免这种情况 EventHandler<LoggerEventArgs> l = Log; if (l != null) l(this, new LoggerEventArgs(priority, msg)); } }
在这里AddMsg()是触发事件的方法,LoggerEventArgs类中定义了事件的优先级和消息内容,委托则为事件处理函数定义了签名。在Logger类内部,事件自动Log定义了事件处理函数。编译器看到这个字段后会自动创建对应的Add和Remove操作符,编译器生成的代码和下面类似:
public class Logger { private EventHandler<LoggerEventArgs> log; public event EventHandler<LoggerEventArgs> Log { add { log = log + value; } remove { log = log - value; } } public void AddMsg(int priority, string msg) { EventHandler<LoggerEventArgs> l = log; if (l != null) l(this, new LoggerEventArgs(priority, msg)); } }
或者我们可以直接查看IL代码:
View Code
public sealed class Logger { private static Dictionary<string, EventHandler<LoggerEventArgs>> Handlers = new Dictionary<string, EventHandler<LoggerEventArgs>>(); static public void AddLogger(string system, EventHandler<LoggerEventArgs> ev) { if (Handlers.ContainsKey(system)) Handlers[system] += ev; else Handlers.Add(system, ev); } static public void RemoveLogger(string system, EventHandler<LoggerEventArgs> ev) { Handlers[system] -= ev; } static public void AddMsg(string system, int priority, string msg) { if (!string.IsNullOrEmpty(system)) { EventHandler<LoggerEventArgs> l = null; Handlers.TryGetValue(system, out l); LoggerEventArgs args = new LoggerEventArgs(priority, msg); if (l != null) l(null, args); //空字符串意味着接收所有消息 l = Handlers[""] as EventHandler<LoggerEventArgs>; if (l != null) l(null, args); } } }
小节
事件提供了一种标准的机制来通知侦听者。.NET的事件模式使用了事件语法来实现观察者模式。任意数量的客户对象都可以将自己的处理函数注册到事件上,然后处理这些事件。这些客户对象不需要在编译器就给出,事件也不必非有订阅者才能正常工作。在C#中使用事件可以降低发送者和可能的通知接收者之间的耦合。发送者完全可以独立于接收者进行开发。事件是实现广播类型行为的标准方式。
相关文章推荐
- 《Effective C#》读书笔记——条目22:通过定义并实现接口替代继承<使用C#表达设计>
- 《Effective C#》读书笔记——条目27:让类型支持序列化<使用C#表达设计>
- 《Effective C#》读书笔记——条目24:用委托实现回调<使用C#表达设计>
- 《Effective C#》读书笔记——条目26:避免返回对内部类对象的引用<使用C#表达设计>
- 《Effective C#》读书笔记——条目23:理解接口方法和虚方法的区别<使用C#表达设计>
- 《Effective C#》读书笔记——条目28:提供粗粒度的互联网API<使用C#表达设计>
- 《Effective C#》读书笔记——条目21:限制类型的可见性<使用C#表达设计>
- 《Effective C#》读书笔记——条目10:使用可选参数减少方法重载的数量<C#语言习惯>
- 《Effective C#》读书笔记——条目1:使用属性而不是可访问的数据成员<C#语言习惯>
- 《Effective C#》读书笔记——条目3:推荐使用is或as而不是强制转换类型<C#语言习惯>
- 《Effective C#》读书笔记——条目4:使用Conditional特性而不是#if条件编译<C#语言习惯>
- 《Effective C#》读书笔记——条目5:为类型提供ToString()方法<C#语言习惯>
- 《Effective C#》读书笔记——条目8:推荐使用查询语法而不是循环<C#语言习惯>
- 《Effective C#》读书笔记——条目6:理解几个等同性判断之间的关系<C#语言习惯>
- 转:《Effective C#》读书笔记——条目17:实现标准的销毁模式<.NET资源管理>
- 《Effective C#》读书笔记——条目11:理解短小方法的优势<C#语言习惯>
- 改进C#代码之25:用事件模式实现通知
- 《Effective C#》读书笔记——条目2:用运行时常量而不是编译期常量<C#语言习惯>
- 《Effective C#》读书笔记——条目17:实现标准的销毁模式<.NET资源管理>
- 《Effective C#》快速笔记(三)- 使用 C# 表达设计