最近在写一个关于数据采集的程序,用到了事件,整理一下,当做是做的笔记。
直接上代码。
public interface ICollector<T> { IList<T> Collect(string url); } public class TmallCollector : ICollector<Product> { public IList<Product> Collect(string url) { throw new NotImplementedException(); } } public class Product { //some property } class Program { static void Main(string[] args) { TmallCollector Collector = new TmallCollector(); var result = Collector.Collect("http://list.tmall.com/search_product.htm?cat=50024400"); //to save data } }
简要说明:接口ICollector是采集器的方法约束,其中方法Collect需要传递一个URL地址,然后就可以返回我们希望得到的数据了,入口函数Main中实例化了一个TmallCollector类,并调用了方法Collect,给定的URL是获取天猫商品的地址,最后我们就可以将采集到的数据保存在我们的数据库中了。
但是这样有一个很大的问题,得到的数据集result将非常庞大(调试的时候还由于内存不足,导致我的VS出现了一些设计器的问题,当然在真正的程序中我是使用的WinForm写的)。
问题分析:
出现这个问题的原因在于我们将所有采集到的数据全部留在了内存中,如果能够实现这样的目的——采集一条数据就保存到数据库中,然后再由GC去释放,这样就保证了内存的合理使用。解决这个问题的方法我想到了两种——1.直接在Main函数中实现采集过程;2.使用事件机制。很明显第一种解决方案是不符合面向对象的思想的,我们需要重用,必须将各个功能模块分离到不同的类或组件中。
引入事件机制:
public delegate void ItemCollected<T>(T currentItem); public interface ICollector<T> { void Collect(string url); event ItemCollected<T> ItemCollectedEvent; } public class TmallCollector : ICollector<Product> { private Product CurrentItem; public void Collect(string url) { while (this.NextItem()) { ItemCollectedEvent(this.CurrentItem); } } private bool NextItem() { throw new NotImplementedException(); } public event ItemCollected<Product> ItemCollectedEvent; } public class Product { //some property } class Program { static void Main(string[] args) { TmallCollector Collector = new TmallCollector(); Collector.ItemCollectedEvent += Collector_ItemCollectedEvent; Collector.Collect("http://list.tmall.com/search_product.htm?cat=50024400"); } static void Collector_ItemCollectedEvent(Product currentItem) { throw new NotImplementedException(); //to save data } }
说明:ICollector接口中添加了一个事件约束ItemCollectedEvent,Collecte方法也不是直接将采集得到的数据全部直接返回,在Mian函数中,实例化采集器之后订阅了ItemCollectedEvent,该事件的事件处理程序Collect_ItemCollectedEvent的参数currentItem指向当前采集器获取的某个项目,我们可以在该事件处理程序中将数据保存到数据库中。
总结:
事件的关键之处在于声明、触发和订阅。首先,声明的时候使用关键字event,类型为一个委托,该委托类型决定了事件处理程序的参数和返回类型;其次,在一个适当的地方触发该事件,这里的ItemCollectedEvent表示某个项目被采集器采集到了的事件,所以我在那里触发了该事件,并将采集到的项目作为参数传递了过去;最后,客户端订阅事件。
事件的实质:(个人想法)
事件和委托都是源于这样的目的:在我们实现某个功能模块的时候,不能确定其内部的所有行为(如这里的采集器不能确定采集到数据后该如何保存数据),于是暴露出一个事件,最终的行为由类的使用者去实现。