Sunday, January 27, 2008

understand event of c#

Keywords: Event, delegate, Publish and subscribe pattern


我打算设计一个程序, 它能一次性美化一个目录之下所有的xml文件, 在UI上, 要求能显示处理的进度(即每处理完一个xml文件, UI的ProgressBar就变化一下). 美化xml的由SourcePackage类的BeautifyFiles方法完成, 而UI是另一个类FormMain. 显然这要处理SourcePackage类和FormMain类之间的通讯问题. 怎样解决这个问题呢? 这其实这是一个如何解耦的问题.


如果你学过C语言, 你会很自然想到使用回调函数, 即在FormMain类中新增一个方法, 比如UpdateProgressBar(), 然后将UpdateProgressBar()这个函数传给SourcePackage.BeautifyFiles()方法. 这确实是一个解决方法. 即将一个被调用者传给调用者. 这种方法直接有效, 但耦合度很高.


另一个方案是, 采用Publish-Subscribe模式, 使用这个模式, 首先要定义一个通知, 即发布什么, 以及订阅什么. 就本实例来讲, 这个通知就是一个xml文件已经被处理完了, 在UI类中, 订阅这个通知, 然后再编写这个通知的一个Handler函数(即进行更新进度条).


C#就是采用Publish-Subscribe来实现Event, 因为C#Delegate支持多播, 所以非常完美地实现了Publish-subscribe; 如果你使用C++, 也可采用了Observer模式来实现Event. 我们用C#的Event来实现设计要求. 设计的详细思路是:


在Publish方(Publish方多数为一个服务的服务端), 需要完成的工作是:


(1) 如果需要的话,使用delegate来定义通知类型, (一般的EventHandler已经在Framework中定义了, 特殊的Handler需要自定义)


public delegate void OneXmlProcessedEventHandler(object sender, ProcessCountEventArgs e);


(2) 在SourcePackage类中, 使用event关键字来发布通知.


public event OneXmlProcessedEventHandler OneXmlProcessed;


(3) 在SourcePackage.BeautifyFiles()触发这个通知的Handler.


foreach (Stream fileStream in m_ListFileStream)


{


m_FileBeautifier.FileStream = fileStream;


m_FileBeautifier.Beautify();


if (OneXmlProcessed != null)


{


eventArgs.ProcessedIndex = eventArgs.ProcessedIndex+1 ;


OneXmlProcessed(this, eventArgs);


}


}


在Subscribe方(Subscribe方多为服务的消费端), 需要完成的工作是:


(1) 订阅这个通知


SourcePackage sourcePackage = new SourcePackage(packageFullName);


sourcePackage.OneXmlProcessed += new OneXmlProcessedEventHandler(OneXmlProcessedHandler);



(2) 编写通知Handler


void OneXmlProcessedHandler(object sender, ProcessCountEventArgs e)


{


const string progressIndicator="{0} of {1} is proceeded!" ;


toolStripStatusLabel1.Text = string.Format(progressIndicator, e.ProcessedIndex, e.FileCount);


toolStripProgressBar1.Value = e.ProcessedIndex;


Application.DoEvents();


}







No comments: