Effective C# Item 22: Define Outgoing Interface With Events

Effective C# Item 22: Define Outgoing Interface With Events

      事件为我们的类型定义了对外的接口。事件是通过委托来提供类型安全的函数签名。由于大部分情况下我们使用委托的例子都是事件,这使得我们容易将二者混为一谈。在Item 21中,我们举了不定义事件的委托的例子。当我们的类型在系统中必须与多个客户程序进行信息交互时,我们应考虑当使用事件。

      我们来考虑一个简单的例子。我们创建一个日志类来调度应用程序中的所有消息。它会接受应用程序资源的所有消息并将其调度给感兴趣的监听者。这个监听者可能是控制台,数据库,系统日志或者其它的什么机制。我们可以像下面这样定义这个类,创建一个事件来处理消息。

None.gif      public   class  LoggerEventArgs : EventArgs
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
public readonly string Message;
InBlock.gif        
public readonly int Priority;
InBlock.gif
InBlock.gif        
public LoggerEventArgs(int p, string m)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Priority 
= p;
InBlock.gif            Message 
= m;
InBlock.gif            Console.WriteLine(
"loggereventArgs ");
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

None.gif
None.gif    
public   delegate   void  AddMessageEventHandler( object  sender, LoggerEventArgs msg);
None.gif
None.gif    
public   class  Logger
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private static Logger _theOnly = null;
InBlock.gif        
static Logger()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            _theOnly 
= new Logger();
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public Logger Singleton
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _theOnly;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public event AddMessageEventHandler Log;
InBlock.gif
InBlock.gif        
public void AddMsg(int priority, string msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            AddMessageEventHandler l 
= Log;
InBlock.gif            
if (l != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                l(
nullnew LoggerEventArgs(priority, msg));
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      我定义了LoggerEventArgs来保存事件的优先级和消息。委托为特定事件的句柄定义了签名。在Logger类内部声明的事件字段定义了事件的句柄。编译器可以识别这些public的事件定义并自动为我们生成add和remove操作符。生成的代码和我们下面所示的代码是相同的:

None.gif      public   class  Logger
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private event AddMessageEventHandler _Log;
InBlock.gif        
public event AddMessageEventHandler Log
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            add
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                _Log 
+= _Log + value;
ExpandedSubBlockEnd.gif            }

InBlock.gif            remove
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                _Log 
-= _Log - value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif
InBlock.gif        
public void AddMsg(int priority, string msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            AddMessageEventHandler l 
= _Log;
InBlock.gif            
if (l != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                l(
nullnew LoggerEventArgs(priority, msg));
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      C#编译器会自动为我们的public事件添加add和remove。这种声明方法更加简洁,易于理解和掌握,而且更为准确。当我们在类中创建事件时,应当将事件声明为public让编译器为我们生成add和remove属性。当然如果我们需要为其添加一些额外的条件约束的话,也可以手工来完成这些操作。

      事件不关心任何潜在的监听者。下面这个类示例将所有消息发送至输出标准错误信息的控制台:

None.gif class  ConsoleLogger
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private static Logger logger = new Logger();
InBlock.gif        
static ConsoleLogger()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            logger.Log 
+= new AddMessageEventHandler(Logger_Log);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private static void Logger_Log(object sender, LoggerEventArgs msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Console.Error.Write(
"{0}:\t{1}", msg.Priority.ToString(), msg.Message);
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      而下例中的类将消息输出到系统事件日志:

None.gif      class  EventLogger
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private static Logger logger = new Logger();
InBlock.gif
InBlock.gif        
private static string eventSource;
InBlock.gif        
private static EventLog logDest;
InBlock.gif        
static EventLogger()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            logger.Log 
+= new AddMessageEventHandler(Event_Log);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public static string EventSource
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return eventSource;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                eventSource 
= value;
InBlock.gif                
if (!EventLog.SourceExists(eventSource))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    EventLog.CreateEventSource(eventSource, 
"ApplicationEventLogger");
ExpandedSubBlockEnd.gif                }

InBlock.gif                
if (logDest != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    logDest.Dispose();
ExpandedSubBlockEnd.gif                }

InBlock.gif                logDest 
= new EventLog();
InBlock.gif                logDest.Source 
= eventSource;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
static void Event_Log(object sender, LoggerEventArgs msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (logDest != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                logDest.WriteEntry(msg.Message, EventLogEntryType.Information, msg.Priority);
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      一旦某些特定的情况发生,事件处理会通知所有感兴趣的客户端。Logger类不需要提前了解任何对此事件感兴趣的对象信息。

      Logger类只包含唯一的一个事件。但是有些类包含了大量的事件(例如大部分的窗体控件)。在这种情况下,为每个事件单独创建一个事件字段的做法就有些欠妥,通常在应用程序中每次只有一少部分事件会被确实的使用。当我们遇到这种情况时,我们可以修改创建事件的方法,使其在运行阶段动态生成这些事件对象。

      这个Logger类的扩展版本包含了一个System.ComponentModel.EventHandlerList容器来储存所有可能被唤起的事件对象。新版本的AddMsg()方法通过一个string型的参数来辨别子系统需要处理的消息。一旦子系统有任何的监听者,那么这个对应的事件将被唤起。

None.gif      public   class  Logger
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private static EventHandlerList handlers = new EventHandlerList();
InBlock.gif
InBlock.gif        
public static void AddLogger(string system, AddMessageEventHandler ev)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            handlers[system] 
= ev;
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public static void RemoveLogger(string system)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            handlers[system] 
= null;
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public static void AddMsg(string system, int priority, string msg)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (!string.IsNullOrEmpty(system))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                AddMessageEventHandler l 
= handlers[system] as AddMessageEventHandler;
InBlock.gif                LoggerEventArgs args 
= new LoggerEventArgs(priority, msg);
InBlock.gif                
if (l != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    l(
null, args);
ExpandedSubBlockEnd.gif                }

InBlock.gif                l 
= handlers[""as AddMessageEventHandler;
InBlock.gif                
if (l != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    l(
null, args);
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      这个新的例子将所有的事件句柄存储在EventHandlerList集合中。当一个新的事件对象被创建后,对于来自于同一个子系统的后续请求将返回同一个事件对象。如果我们开发一个包含大量事件的类,你应当考虑使用事件句柄集合来处理。在.Net Framework中,System.Windows.Forms.Control类使用一种更加复杂的实现方法来对应复杂的事件字段。每个事件字段内部包含一个对象集合来添加和移除特定的句柄。

      当我们在接口中为类定义事件时,我们应当明白任何客户端程序都可以将句柄关联到上面来。那些客户端程序并不需要了解编译时的状态。使用C#中的事件可以消除发送者和可能接收者之间的耦合关系。发送者可以被开发成与接收者无关的模式。事件是消息传递的一种标准的模式。

      译自   Effective C#:50 Specific Ways to Improve Your C#                      Bill Wagner著

      回到目录

转载于:https://www.cnblogs.com/aiyagaze/archive/2006/12/22/596225.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值