改进C#代码之25:用事件模式实现通知
事件提供了一种标准的机制来通知监听者。.NET的事件模式使用了事件语法来实现观察者模式。任意数量的客户对象都可以将自己的处理函数注册到事件上,然后处理这些事件。这些客户对象不需要再编译期就给出。时间也不必非要有订阅者才能正常工作。在C#中使用事件可以降低发送者和可能的通知接受者之间的耦合。发送者可以完全独立于接收者进行开发。事件是实现广播类型行为信息的标准方式。
下面按照使用场景的不同,简单列举三种事件模式的实现方式:
0. 公共代码部分
1 /// <summary>
2 /// 日志参数类
3 /// </summary>
4 public class LoggerEventArgs : EventArgs
5 {
6 public string Message { get; private set; }
7 public int Priority { get; private set; }
8
9 public LoggerEventArgs(int p, string m)
10 {
11 Priority = p;
12 Message = m;
13 }
14 }
3 /// </summary>
4 public class LoggerEventArgs : EventArgs
5 {
6 public string Message { get; private set; }
7 public int Priority { get; private set; }
8
9 public LoggerEventArgs(int p, string m)
10 {
11 Priority = p;
12 Message = m;
13 }
14 }
1. 最常用方式
1 /// <summary>
2 /// 日志类(第一版):最常见的使用方法,适用于单一调用者情况。
3 /// </summary>
4 public class Logger
5 {
6 /// <summary>
7 /// 内部事件句柄
8 /// </summary>
9 public event EventHandler<LoggerEventArgs> Log;
10 /// <summary>
11 /// 内部日志单键实例
12 /// </summary>
13 private static Logger theOnly = null;
14 public static Logger Singleton
15 {
16 get { return theOnly; }
17 }
18
19 private Logger()
20 { }
21
22 static Logger()
23 {
24 theOnly = new Logger();
25 }
26
27 /// <summary>
28 /// 添加日志信息
29 /// </summary>
30 /// <param name="priority"></param>
31 /// <param name="msg"></param>
32 public void AddMsg(int priority, string msg)
33 {
34 // 该临时变量可预防多线程环境中的竞争条件
35 EventHandler<LoggerEventArgs> l = Log;
36 // 执行事件方法
37 if (l != null)
38 {
39 l(this, new LoggerEventArgs(priority, msg));
40 }
41 }
42 }
3 /// </summary>
4 public class Logger
5 {
6 /// <summary>
7 /// 内部事件句柄
8 /// </summary>
9 public event EventHandler<LoggerEventArgs> Log;
10 /// <summary>
11 /// 内部日志单键实例
12 /// </summary>
13 private static Logger theOnly = null;
14 public static Logger Singleton
15 {
16 get { return theOnly; }
17 }
18
19 private Logger()
20 { }
21
22 static Logger()
23 {
24 theOnly = new Logger();
25 }
26
27 /// <summary>
28 /// 添加日志信息
29 /// </summary>
30 /// <param name="priority"></param>
31 /// <param name="msg"></param>
32 public void AddMsg(int priority, string msg)
33 {
34 // 该临时变量可预防多线程环境中的竞争条件
35 EventHandler<LoggerEventArgs> l = Log;
36 // 执行事件方法
37 if (l != null)
38 {
39 l(this, new LoggerEventArgs(priority, msg));
40 }
41 }
42 }
2. 针对事件数量多的情况
1 /// <summary>
2 /// 日志类(第二版):适用于包含事件数量非常多的情况,即添加了一个事件容器,避免因多事件导致的设计臃肿。
3 /// </summary>
4 public sealed class Logger
5 {
6 /// <summary>
7 /// 事件容器
8 /// </summary>
9 private static System.ComponentModel.EventHandlerList Handlers = new System.ComponentModel.EventHandlerList();
10
11 /// <summary>
12 /// 添加事件
13 /// </summary>
14 /// <param name="system"></param>
15 /// <param name="ev"></param>
16 public static void AddLogger(string system, EventHandler<LoggerEventArgs> ev)
17 {
18 Handlers.AddHandler(system, ev);
19 }
20
21 /// <summary>
22 /// 清除事件
23 /// </summary>
24 /// <param name="system"></param>
25 /// <param name="ev"></param>
26 public static void RemoveLogger(string system, EventHandler<LoggerEventArgs> ev)
27 {
28 Handlers.RemoveHandler(system, ev);
29 }
30
31 /// <summary>
32 /// 添加日志信息
33 /// </summary>
34 /// <param name="system"></param>
35 /// <param name="priority"></param>
36 /// <param name="msg"></param>
37 public static void AddMsg(string system, int priority, string msg)
38 {
39 if (!string.IsNullOrEmpty(system))
40 {
41 // 根据key获取相应的事件
42 EventHandler<LoggerEventArgs> l = Handlers[system] as EventHandler<LoggerEventArgs>;
43 // 事件参数
44 LoggerEventArgs args = new LoggerEventArgs(priority, msg);
45 if (l != null)
46 {
47 l(null, args);
48 }
49 // 若不存在,执行默认事件
50 l = Handlers[""] as EventHandler<LoggerEventArgs>;
51 if (l != null)
52 {
53 l(null, args);
54 }
55 }
56 }
57 }
3 /// </summary>
4 public sealed class Logger
5 {
6 /// <summary>
7 /// 事件容器
8 /// </summary>
9 private static System.ComponentModel.EventHandlerList Handlers = new System.ComponentModel.EventHandlerList();
10
11 /// <summary>
12 /// 添加事件
13 /// </summary>
14 /// <param name="system"></param>
15 /// <param name="ev"></param>
16 public static void AddLogger(string system, EventHandler<LoggerEventArgs> ev)
17 {
18 Handlers.AddHandler(system, ev);
19 }
20
21 /// <summary>
22 /// 清除事件
23 /// </summary>
24 /// <param name="system"></param>
25 /// <param name="ev"></param>
26 public static void RemoveLogger(string system, EventHandler<LoggerEventArgs> ev)
27 {
28 Handlers.RemoveHandler(system, ev);
29 }
30
31 /// <summary>
32 /// 添加日志信息
33 /// </summary>
34 /// <param name="system"></param>
35 /// <param name="priority"></param>
36 /// <param name="msg"></param>
37 public static void AddMsg(string system, int priority, string msg)
38 {
39 if (!string.IsNullOrEmpty(system))
40 {
41 // 根据key获取相应的事件
42 EventHandler<LoggerEventArgs> l = Handlers[system] as EventHandler<LoggerEventArgs>;
43 // 事件参数
44 LoggerEventArgs args = new LoggerEventArgs(priority, msg);
45 if (l != null)
46 {
47 l(null, args);
48 }
49 // 若不存在,执行默认事件
50 l = Handlers[""] as EventHandler<LoggerEventArgs>;
51 if (l != null)
52 {
53 l(null, args);
54 }
55 }
56 }
57 }
3. 针对事件数量多的情况(泛型版本)
1 /// <summary>
2 /// 日志类(第三版):适用于多事件情况的泛型版本。主要优势是降低了转型/转换的工作,但增加了一些用来映射事件的代码。
3 /// </summary>
4 public sealed class Logger
5 {
6 /// <summary>
7 /// 事件字典
8 /// </summary>
9 private static Dictionary<string, EventHandler<LoggerEventArgs>> Handlers = new Dictionary<string, EventHandler<LoggerEventArgs>>();
10
11 /// <summary>
12 /// 添加事件
13 /// </summary>
14 /// <param name="system"></param>
15 /// <param name="ev"></param>
16 static public void AddLogger(string system, EventHandler<LoggerEventArgs> ev)
17 {
18 if (Handlers.ContainsKey(system))
19 {
20 Handlers[system] += ev;
21 }
22 else
23 {
24 Handlers.Add(system, ev);
25 }
26 }
27
28 /// <summary>
29 /// 清除事件
30 /// </summary>
31 /// <param name="system"></param>
32 /// <param name="ev"></param>
33 static public void RemoveLogger(string system, EventHandler<LoggerEventArgs> ev)
34 {
35 Handlers[system] -= ev;
36 }
37
38 /// <summary>
39 /// 添加日志信息
40 /// </summary>
41 /// <param name="system"></param>
42 /// <param name="priority"></param>
43 /// <param name="msg"></param>
44 static public void AddMsg(string system, int priority, string msg)
45 {
46 if (string.IsNullOrEmpty(system))
47 {
48 // 从字典中获取事件
49 EventHandler<LoggerEventArgs> l = null;
50 Handlers.TryGetValue(system, out l);
51 // 事件参数
52 LoggerEventArgs args = new LoggerEventArgs(priority, msg);
53 // 执行事件
54 if (l != null)
55 {
56 l(null, args);
57 }
58 // 若不存在,则尝试执行默认事件
59 l = Handlers[""] as EventHandler<LoggerEventArgs>;
60 if (l != null)
61 {
62 l(null, args);
63 }
64 }
65 }
66 }
3 /// </summary>
4 public sealed class Logger
5 {
6 /// <summary>
7 /// 事件字典
8 /// </summary>
9 private static Dictionary<string, EventHandler<LoggerEventArgs>> Handlers = new Dictionary<string, EventHandler<LoggerEventArgs>>();
10
11 /// <summary>
12 /// 添加事件
13 /// </summary>
14 /// <param name="system"></param>
15 /// <param name="ev"></param>
16 static public void AddLogger(string system, EventHandler<LoggerEventArgs> ev)
17 {
18 if (Handlers.ContainsKey(system))
19 {
20 Handlers[system] += ev;
21 }
22 else
23 {
24 Handlers.Add(system, ev);
25 }
26 }
27
28 /// <summary>
29 /// 清除事件
30 /// </summary>
31 /// <param name="system"></param>
32 /// <param name="ev"></param>
33 static public void RemoveLogger(string system, EventHandler<LoggerEventArgs> ev)
34 {
35 Handlers[system] -= ev;
36 }
37
38 /// <summary>
39 /// 添加日志信息
40 /// </summary>
41 /// <param name="system"></param>
42 /// <param name="priority"></param>
43 /// <param name="msg"></param>
44 static public void AddMsg(string system, int priority, string msg)
45 {
46 if (string.IsNullOrEmpty(system))
47 {
48 // 从字典中获取事件
49 EventHandler<LoggerEventArgs> l = null;
50 Handlers.TryGetValue(system, out l);
51 // 事件参数
52 LoggerEventArgs args = new LoggerEventArgs(priority, msg);
53 // 执行事件
54 if (l != null)
55 {
56 l(null, args);
57 }
58 // 若不存在,则尝试执行默认事件
59 l = Handlers[""] as EventHandler<LoggerEventArgs>;
60 if (l != null)
61 {
62 l(null, args);
63 }
64 }
65 }
66 }
至于选择哪种方式来实现,就要看具体的应用场景了;此外,大多数时候我们都会使用匿名委托来声明回调函数,或事件委托,所以会导致代码的运行时态有一些小波折,尤其在读别人的代码的时候,这样的情况很普遍,会不会有更好的办法来让这种回调和委托比较容易跟踪?这个还在思考中……
希望能有所帮助~