用一个缓存控制的例子来让你了解委托和事件的真正意义以及两者的区别

前几日在CSDN论坛上看到一个关于委托顿悟的帖子 但是总感觉呢 用一些生活常识的抽象进行的解释总让人觉得和实际开发工作脱节 对于不懂的人来说可能仍旧不懂 懂的人呢觉得都是错误(原帖挑刺的大神很多啊)重点是实际写代码的时候呢可能还是不知道什么时候改用委托 该怎么用(所谓理论和实际的代沟)如果是事件的话 估计更加不知道怎么去用了 发个博客算是以一种实际工作角度出发的例子来说下委托的意义 以及事件和委托的区别

设想你做了一个三层架构 其中用到了缓存(非页面缓存) 对于缓存的控制呢我一般是放在BLL层 不过我这边叫他Service层
对于缓存我们要做哪些控制呢?
首先 对于第一次访问的数据 我们会把这些数据放到我们的ASP.NET缓存中 对于执行增删改了的操作 我们需要对缓存进行更新 但是具体怎么进行控制 对于基类是不好预测的 那么我们就可以把它放到子类里面 进行具体实现的推迟 这样子就衍生出了另一种做法 就是把这些控制方法定义为抽象方法 让子类去实现 但是!!有些数据量不大的操作 我并不需要缓存 而且可能这些控制方法也不是子类来定义的 那么我们的这种将实现推迟到子类的方法就行不通了 这个时候 就是委托起作用的时候了 其实对于委托我个人的理解是这样的 把不知道怎么做的事情 交给别人去做 你只要调用他就可以了 即:回调

下面上代码: 此层的接口定义是这样的 其中的QueryResult<T>类其实就是对操作结果的一个封装 IQueryServiceBase

 

public interface IServiceBase<T>
    {
        List<T> Query(int startIndex, int pageSize, ref int totalCount);//分页查询
        List<T> GetAll();
        T GetByID(int id);
    }

 

以上都是完整的接口定义 下面讲的时候我只针对一个方法 Query来讲 毕竟都是差不多的
先还是看下一个实现了接口的类ServiceBase所包含的事件成员:

#region Event

        public delegate void OnDataChanged(object sender, DataChangedEventArg<T> e);//定义一个委托类型 他可以作为事件

        public event OnDataChanged DataChanged;//定义一个事件 该事件类型呢就是我们刚刚定义的委托 一会儿我们会解释为什么要用事件而不是直接将委托暴露出去

        protected void CallEvent(List<T> eventData)//事件的调用方法 eventData就是我们的事件参数 更好的理解呢 应该是 事件发生时候的上下文
        {
            if (this.DataChanged != null)
            {
                var eventArg = new DataChangedEventArg<T>(eventData);//事件参数必须从EventArg类基础 你可以为他添加别的属性 代表事件发生时候你要传递给事件订阅者的事件上下文 本例中 实际上就是你在进行CRUD操作时所涉及到的数据
                this.DataChanged(this, eventArg);
            }
        }

        #endregion

上面的代码定义了我们的事件 要记住事件就是一种特殊的委托 说他特殊 是因为他必须有固定的方法签名 而且不可被订阅者直接调用 通俗点儿 对于事件 我们只可以进行两种操作:订阅与取消订阅  所谓订阅 就是之前提到的 对于事件的发布者(这里的ServiceBase类)不知道怎么完成的操作 由订阅者完成了 但是呢 订阅者不能触发事件 也就是上面的DataChanged 如果你直接在订阅者处(也就是ServiceBase类之外)直接调用ServiceBase的DataChanged事件 编译器是会报错的 这就是刚刚说到的 Event是一种特殊的委托 因为如果是委托 我们是可以直接在ServiceBase之触发这个委托的
总结起来 就是 Event是只可以由事件发布者来触发(注意这里用了触发 而不是调用) 而委托呢?谁都可以触发。
为什么要这么做呢 这里就考虑到我们之前的事件参数 虽然ServiceBase不知道如何进行缓存控制 但是他必须知道哪些数据必须交给具体控制的代码 这里就是事件的第二个参数EventArg的作用 他就负责了将这些数据传递给具体实现的代码。
上面撤了这么多 那么我们来看下事件的触发代码 注意 这里用的是触发 什么时候触发事件 对于事件的订阅者是不知道的 事件的订阅者只是知道如何来完成这个发布者不知道怎么做的事情 而事件的发布者则决定了什么时候来触发事件

 

public virtual List<T> Query(int startIndex, int pageSize, ref int totalCount)
        {
            var returnValue = _search.Query(startIndex, pageSize, ref totalCount);//这个_search变量是ServiceBase的字段 其实就是个数据访问组件 这里也可以理解三层架构的意义 我们在底层数据层只做基础的CRUD 返回一致的对象List,Service层呢则会进行一些别的操作 比如我们的缓存控制
            CallEvent(returnValue);//在执行分页查询的时候 触发了我们的事件 虽然这里形式上是一种方法调用 这里通过将我们查询得到的结果赋值给事件参数 使得具体控制缓存的代码可以利用这些数据对缓存进行更新操作
            return returnValue;
        }

上面的代码是事件触发代码 在我们的ServiceBase里 下面呢就是订阅者的代码 这里呢是一个NewsQueryService类 基础自ServiceBase类 负责新闻查找之类的操作 自然涉及了具体的缓存控制

public NewsQueryService(NewsTemplateQuery repository)//构造方法 参数是数据层的类
            : base(repository)
        {
            this._repository = repository;
            this.DataChanged += new OnDataChanged(NewsService_DataChanged);//订阅事件
        }
        public override List<news> Query(int startIndex, int pageSize, ref int totalCount)
        {
            if(缓存存在)
            {
               return 缓存内容;
            }
            return base.Query(startIndex, pageSize, ref totalCount);//缓存不存在 则执行具体的查询操作 并由于调用了基类的Query方法 所以实际上会触发DataChanged事件 但是必须知道 触发的类是ServiceBase类 而不是这边的NewsQueryService类
        }

        void NewsService_DataChanged(object sender, DataChangedEventArg<news> e)//实现具体的缓存控制操作
        {
            var news = e.Data;//在之前的ServiceBase触发事件的时候传入的数据 保证了事件触发传递的数据只能是由事件发布者提供 在这里 就代表了具体查询到的新闻
            //下面的代码呢 就是一些具体的缓存控制代码 比如说更新缓存 移除缓存 甚至可以做一些延迟绑定之类的操作 具体的代码就不在这里写了
        }

上面既是一个完整的例子 这边呢 ServiceBase和NewsQueryService是父子继承关系 换句话说 利用抽象方法 我们一样可以做到这种延迟实现 那么如果说NewsQueryService包含了一个ServiceBase类 是一个b has a关系呢? 此时就非得用事件来做了 

总结一下 委托的意思呢 我的理解 应该是把不知道怎么做的事情,经常变化的事情封装起来 交给别人去做 你直接调用别人实现了的就可以 虽然用抽象方法也可以做 但是这就形成了一种局限 也就是这个不会做的类和知道怎么做的类必须是一种继承关系 而委托则取消了这种局限 使得任何关系的类都可以实现这种所谓实现的抽象 其实面向对象 设计模式很重要的一点 就是将变化封装 
至于说委托就是函数指针的 实在是害人不浅 没学过C++的能知道函数指针是什么么?而且就算知道了,又有什么用呢?我绝对对于委托这个概念 光知道概念是没有用的 重点是知道怎么用 如何用 毕竟这不是一个简单的语法 而是涉及到很多编程理念的一个东西(lambda表达式其实也就是所谓的匿名委托)
至于事件 他呢是一个特殊的委托 特殊在 他的调用只可以是发布这个委托的类 这样就使得触发时候的上下文可以保证正确(我们的例子中就是需要进行缓存控制时涉及到的数据) 而这也解释了为什么事件必须要有两个特殊的参数 一个就是我们实际的事件触发者sender,另一个就是触发事件时包含的数据EventArg

头一次写这种文章 还请各路看官拍砖!!!

 

 

 

 

转载于:https://www.cnblogs.com/xboxeer/archive/2012/06/09/2543484.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值