上一篇博客讲到了LinQ和lambda的常用方法 还有很多我们未知但c#设计团队已经为我们封装好的类和方法。随着我们不断的熟悉C#语言,渐渐的就会接触到其他的知识点,委托、事件、反射、线程、同步,异步、IO、套接字。。。这些东西我们平常用到的不多,都是些概念性的东西,也许是因为不熟悉而可以回避了使用这些东西,不可否认的是 就算不用这些我们依然能想到问题的解决办法。但是几乎所有语言都会有这些概念,因为在某些场景它们能发挥不可思议的能力。
其实我到现在还是没有掌握委托和事件,在工作或者设计中也尽量回避使用,但如果想走技术这条路,这块硬骨头必须趁早啃了它。我们一直在疑惑:为什么要用委托 很想问别人 什么场景下用到委托 反正我是没有得到我想要的答案 所以也不能深刻的体会它的妙用。本文将讲诉普通的委托,事件的委托,有参 无参 有返回值的 以上都是同步的委托,异步的到后面的异步和套接字再讲。
基本语法
//无返回值 public delegate void delegateName() ;//无参 无返回值 public delegate void delegateName(int a,int b) ;//2个参数 无返回值 public delegate void delegateName(object o) ;//任意类型 无返回值 public delegate void delegateName(object o,EventArgs args) ;//任意类型,第2个参数为一个事件对象或者基础了该事件对象的子类 无返回值
//有返回值 public delegate bool delegateName() ;//无参 有返回值 public delegate bool delegateName(object) ;//与参 有返回值 这个是不是有点熟悉呢 它就是我们常用的userlist.where()方法 要求传参的委托 返回布尔 带一个object参数或多个原型如下 Func<TSource, bool> predicate 最后一个参数为返回值 public delegate object delegateName(int a,int b) ;//2个参数 有返回值 public delegate List<object> delegateName(object o) ;//任意类型 有返回值 public delegate bool delegateName(object o,EventArgs args) ;//任意类型,第2个参数为一个事件对象或者基础了该事件对象的子类 有返回值
基本使用大家可能都略知一二,那就来点案例加深理解,来个大家熟悉的场景 QQ空间。
案例分析
场景如下:
QQ空间发布说说大家都常用,每当我们更新了说说或者日志,好友进空间的时候总能知道是谁更新了说说。但他是怎么知道你更新了的呢?
分析:
实现1:在每个用户所属的表里面存有每个好友的上一次更新的说说编号,当进空间初始化时根据每个编号去和好友空间最新的编号对比,获取大于当前编号的日志,如果好友上百估计很慢
实现2:用户每次发布说说时,遍历他所有好友,主动插入一条说说编号到他好友说说更新表里面或者缓存表中,当好友进空间时 根据未读说说状态的编号去查询加载出来
...
实现方式多种多样,具体用的什么方法我也猜不出来 如果有熟悉的或者来自鹅厂的大神请给我们解惑!
接下来的案例代码是以第二种方式来实现观察者模式的(发布者与订阅者)
功能分析
在这个场景中,有当前QQ用户发布说说(发布者),他所有的好友(订阅者),还有说说(日志)实体
这里面会出现3个对象
User QQ Log
public class User { public User() { } public event PubLogHanler Handler; public User(int id,QQ qq) { ID = id; QQ = qq; } public int ID { get; set; } //用户的QQ,里面包含好友列表等信息 public QQ QQ { get; set; } //发说说(用户方法) public void PubLog(Log log) { Console.WriteLine("发布说说中..."); StringBuilder sb = new StringBuilder(); sb.AppendLine("===================================="); sb.AppendLine(log.Title); sb.AppendLine(" "+log.Content); sb.Append("\t"+log.Author.ToString()+"\t"); sb.AppendLine(log.PubTime); sb.AppendLine("===================================="); Console.WriteLine(sb.ToString()); //触发事件 OnPubLog(this, new PubLogHanlerArgs(log)); } public void OnPubLog(object o, PubLogHanlerArgs args) { if (Handler != null) { Handler(o, args); } } }
然后是QQ实体对象,它包含好友列表等信息 这里只列举了好友列表
public class QQ { public QQ(){} public QQ(int id,List<User>list) { ID = id; FriendList = list; } //QQ号 public int ID { get; set; } //好友列表 public List<User> FriendList { get; set; } }
接下来是说说实体对象
/// <summary> /// 说说类 /// </summary> public class Log { public Log() { } public Log(string title, string content, int author, string time) { Title = title; Content = content; Author = author; PubTime = time; } public string Title { get; set; } public string Content { get; set; } //(QQID)作者 public int Author { get; set; } public string PubTime { get; set; } }
调用类里面弄点初始化数据
User user1 = new User(1, new QQ(56521321, new List<User>() { })); User user2 = new User(1, new QQ(28121315, new List<User>() { })); User user3 = new User(1, new QQ(565221, new List<User>() { })); //new 一个用户 拥有一个QQ号和3个好友 User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 }));
如果是普通的 肯定就是直接发说说了
//发说说 currUser.PubLog(new Log("我的第一个说说","说说开通了可以分享咯",currUser.QQ.ID,DateTime.Now.ToString("M.d HH:mm:ss")));
//循环遍历所有好友 foreach (var item in currUser.QQ.FriendList) { ////推送 存储日志编号到好友的待加载列表里面 }
这样其实也算是实现功能了,如果要求不高那就提交代码可以领盒饭了。
好吧 到现在为止也没委托啥事呀!!!
委托的定义和快递的职责是一样一样的:发件人委托给快递,快递再投送给接收者。你说其实他也可以直接给接收者,但他为什么要委托快递呢 懒!就是懒 哈哈 这样理解也是奇葩了 !
你让我说说为啥委托非用不可 那我还真答不上来,就像我们也可以不用快递一样,只不过代价就大了,可以想象那场景,网上卖件衣服都要送出国再自己回来,那酸爽。。。
既然机智的选择用快递,那我们就定义一个送快递的吧
public delegate void delegateEMS(User o, Log log); //再定义一个快递送快递的方法 public static void PubEms(User user,Log log) { foreach (var item in user.QQ.FriendList) { Console.WriteLine("已推送说说标题为: " + log.Title + " 的说说给:" + item.QQ.ID); //投送 发往指定人 } }
然后就是调用
User user1 = new User(1, new QQ(56521321, new List<User>() { })); User user2 = new User(1, new QQ(28121315, new List<User>() { })); User user3 = new User(1, new QQ(565221, new List<User>() { })); //new 一个用户 拥有一个QQ号和3个好友 User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 })); Log temp = new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")); //发说说 currUser.PubLog(temp); delegateEMS ems = new delegateEMS(PubEms); ems(currUser, temp);
然后就没有然后了 等着收货的给差评吧!
好吧说跑题了,继续回来说说QQ空间的事,刚才讲快递是为了加深大家对于委托的理解,我们用到了普通的委托有参无返回 但是发现每发一篇博客就要多写一次 大量重复的代码 我们接下来改为事件+委托
委托的进阶使用 事件+委托
事件可以说是无处不在 鼠标点击事件 移动事件就不说了 之所以用到事件就是为了实现触发器的效果 删除一条记录就邮件通知相关负责人 出现多少个警告也会通知 在我们项目中比较常用
基本语法
public event PubLogHanler Handler; //这里事件类型是自定义的
//定义推送委托 public delegate void PubLogHanler(object o, PubLogHanlerArgs args); //发布日志触发 public class PubLogHanlerArgs : EventArgs { public PubLogHanlerArgs(){} public PubLogHanlerArgs(Log log) { LogInfo = log; } public Log LogInfo { get; set; } }
这里自定义了一个说说发布的事件,它继承EventArgs类,是任何事件的基类,触发一个事件时会记录下说说的信息
public void PubLog(Log log) { Console.WriteLine("发布说说中..."); StringBuilder sb = new StringBuilder(); sb.AppendLine("===================================="); sb.AppendLine(log.Title); sb.AppendLine(" "+log.Content); sb.Append("\t"+log.Author.ToString()+"\t"); sb.AppendLine(log.PubTime); sb.AppendLine("===================================="); Console.WriteLine(sb.ToString()); //触发事件 OnPubLog(this, new PubLogHanlerArgs(log)); } public void OnPubLog(object o, PubLogHanlerArgs args) { if (Handler != null) { Handler(o, args); } }
从上面的代码可以看到 调用发布说说方法后会触发事件,然后事件会去执行推送方法达到触发器的效果
//推送事件 static void currUser_Handler(object o, PubLogHanlerArgs args) { User user = o as User; if(user!=null) { foreach (var item in user.QQ.FriendList) { Console.WriteLine("已推送说说标题为: "+args.LogInfo.Title+" 的说说给:"+item.QQ.ID); //推送 存储日志编号到好友的待加载列表里面 } } }
其实上面只是个方法,只是参数看起来是事件罢了,接下来就是绑定事件
//推送给好友 currUser.Handler += new PubLogHanler(currUser_Handler);
//发说说 currUser.PubLog(new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss"))); currUser.PubLog(new Log("终于上博客园首页啦", "博客园是个学习的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));
事件的妙处就在于可以绑定多个事件 ,叫多播委托。再定义一个事件
//已通知更新缓存 static void UpdateCache_Handler(object o, EventArgs args) { User user = o as User; if (user != null) { Console.WriteLine("\n<*>已通知更新缓存<*>\n"); //已通知更新缓存 } }
调用代码如下
User user1 = new User(1, new QQ(56521321, new List<User>() { })); User user2 = new User(1, new QQ(28121315, new List<User>() { })); User user3 = new User(1, new QQ(565221, new List<User>() { })); //new 一个用户 拥有一个QQ号和3个好友 User currUser = new User(1, new QQ(565214359, new List<User>() { user1, user2, user3 })); //推送给好友 currUser.Handler += new PubLogHanler(currUser_Handler); //通知更新缓存 currUser.Handler += new PubLogHanler(UpdateCache_Handler); //发说说 currUser.PubLog(new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss"))); currUser.PubLog(new Log("终于上博客园首页啦", "博客园是个学习的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss"))); Console.ReadKey();
总结
委托和事件讲到这里也接近尾声了,我这也是临时抱佛脚,在网上看了写实例代码 自己实操了一遍就根据自己的理解发上来了。很多地方估计语句不是太通顺甚至概论都没理明白,如果有错误之处还望大家指正,免得误人子弟!
如果有需要源代码的话我稍后会发上来的。下一篇博客 我会讲一讲反射和线程,并且把我写的一个xml json datatable List 相互转换 的dll和代码分享给大家,请大家关注我的博客http://www.cnblogs.com/jingch/