上一篇博客讲到了LinQ和lambda的常用方法 还有很多我们未知但c#设计团队已经为我们封装好的类和方法。随着我们不断的熟悉C#语言,渐渐的就会接触到其他的知识点,委托、事件、反射、线程、同步,异步、IO、套接字。。。这些东西我们平常用到的不多,都是些概念性的东西,也许是因为不熟悉而可以回避了使用这些东西,不可否认的是 就算不用这些我们依然能想到问题的解决办法。但是几乎所有语言都会有这些概念,因为在某些场景它们能发挥不可思议的能力。

其实我到现在还是没有掌握委托和事件,在工作或者设计中也尽量回避使用,但如果想走技术这条路,这块硬骨头必须趁早啃了它。我们一直在疑惑:为什么要用委托 很想问别人 什么场景下用到委托 反正我是没有得到我想要的答案 所以也不能深刻的体会它的妙用。本文将讲诉普通的委托,事件的委托,有参 无参 有返回值的 以上都是同步的委托,异步的到后面的异步和套接字再讲。

基本语法

  1. //无返回值
  2. public delegate void delegateName() ;//无参 无返回值
  3. public delegate void delegateName(int aint b) ;//2个参数 无返回值
  4. public delegate void delegateName(object o) ;//任意类型 无返回值
  5. public delegate void delegateName(object o,EventArgs args) ;//任意类型,第2个参数为一个事件对象或者基础了该事件对象的子类 无返回值
  1. //有返回值
  2. public delegate bool delegateName() ;//无参 有返回值
  3. public delegate bool delegateName(object) ;//与参 有返回值 这个是不是有点熟悉呢 它就是我们常用的userlist.where()方法 要求传参的委托 返回布尔 带一个object参数或多个原型如下 Func<TSource, bool> predicate 最后一个参数为返回值
  4. public delegate object delegateName(int aint b) ;//2个参数 有返回值
  5. public delegate List<object> delegateName(object o) ;//任意类型 有返回值
  6. public delegate bool delegateName(object o,EventArgs args) ;//任意类型,第2个参数为一个事件对象或者基础了该事件对象的子类 有返回值

基本使用大家可能都略知一二,那就来点案例加深理解,来个大家熟悉的场景 QQ空间。

案例分析

场景如下:

QQ空间发布说说大家都常用,每当我们更新了说说或者日志,好友进空间的时候总能知道是谁更新了说说。但他是怎么知道你更新了的呢?

分析:

实现1:在每个用户所属的表里面存有每个好友的上一次更新的说说编号,当进空间初始化时根据每个编号去和好友空间最新的编号对比,获取大于当前编号的日志,如果好友上百估计很慢

实现2:用户每次发布说说时,遍历他所有好友,主动插入一条说说编号到他好友说说更新表里面或者缓存表中,当好友进空间时 根据未读说说状态的编号去查询加载出来

...

实现方式多种多样,具体用的什么方法我也猜不出来 如果有熟悉的或者来自鹅厂的大神请给我们解惑!

接下来的案例代码是以第二种方式来实现观察者模式的(发布者与订阅者)

功能分析

在这个场景中,有当前QQ用户发布说说(发布者),他所有的好友(订阅者),还有说说(日志)实体

这里面会出现3个对象

User QQ Log

  1. public class User
  2. {
  3. public User() { }
  4.  
  5. public event PubLogHanler Handler;
  6. public User(int id,QQ qq)
  7. {
  8. ID = id;
  9. QQ = qq;
  10.  
  11. }
  12.  
  13. public int ID { get; set; }
  14. //用户的QQ,里面包含好友列表等信息
  15. public QQ QQ { get; set; }
  16.  
  17. //发说说(用户方法)
  18. public void PubLog(Log log)
  19. {
  20. Console.WriteLine("发布说说中...");
  21. StringBuilder sb = new StringBuilder();
  22. sb.AppendLine("====================================");
  23. sb.AppendLine(log.Title);
  24. sb.AppendLine(" "+log.Content);
  25. sb.Append("\t"+log.Author.ToString()+"\t");
  26. sb.AppendLine(log.PubTime);
  27. sb.AppendLine("====================================");
  28. Console.WriteLine(sb.ToString());
  29.  
  30. //触发事件
  31.  
  32. OnPubLog(this, new PubLogHanlerArgs(log));
  33.  
  34. }
  35. public void OnPubLog(object o, PubLogHanlerArgs args)
  36. {
  37. if (Handler != null)
  38. {
  39.  
  40. Handler(o, args);
  41. }
  42.  
  43. }
  44. }

然后是QQ实体对象,它包含好友列表等信息 这里只列举了好友列表

  1. public class QQ
  2. {
  3. public QQ(){}
  4. public QQ(int id,List<User>list)
  5. {
  6. ID = id;
  7. FriendList = list;
  8. }
  9. //QQ号
  10. public int ID { get; set; }
  11. //好友列表
  12. public List<User> FriendList { get; set; }
  13. }

接下来是说说实体对象

  1. /// <summary>
  2. /// 说说类
  3. /// </summary>
  4. public class Log
  5. {
  6.  
  7. public Log() { }
  8. public Log(string title, string content, int author, string time)
  9. {
  10. Title = title;
  11. Content = content;
  12. Author = author;
  13. PubTime = time;
  14. }
  15. public string Title { get; set; }
  16. public string Content { get; set; }
  17. //(QQID)作者
  18. public int Author { get; set; }
  19. public string PubTime { get; set; }
  20.  
  21. }

调用类里面弄点初始化数据

  1. User user1 = , , new List<User>() { }));
  2. User user2 = , , new List<User>() { }));
  3. User user3 = , , new List<User>() { }));
  4. //new 一个用户 拥有一个QQ号和3个好友
  5. User currUser = , , new List<User>() { user1, user2, user3 }));

如果是普通的 肯定就是直接发说说了

  1. //发说说
  2. currUser.PubLog(new Log("我的第一个说说","说说开通了可以分享咯",currUser.QQ.ID,DateTime.Now.ToString("M.d HH:mm:ss")));
  1. //循环遍历所有好友
  2. foreach (var item in currUser.QQ.FriendList)
  3. {
  4. ////推送 存储日志编号到好友的待加载列表里面
  5. }

这样其实也算是实现功能了,如果要求不高那就提交代码可以领盒饭了。

好吧  到现在为止也没委托啥事呀!!!

委托的定义和快递的职责是一样一样的:发件人委托给快递,快递再投送给接收者。你说其实他也可以直接给接收者,但他为什么要委托快递呢 懒!就是懒 哈哈 这样理解也是奇葩了 !

你让我说说为啥委托非用不可 那我还真答不上来,就像我们也可以不用快递一样,只不过代价就大了,可以想象那场景,网上卖件衣服都要送出国再自己回来,那酸爽。。。

既然机智的选择用快递,那我们就定义一个送快递的吧

  1. public delegate void delegateEMS(User o, Log log);
  2. //再定义一个快递送快递的方法
  3. public static void PubEms(User user,Log log)
  4. {
  5. foreach (var item in user.QQ.FriendList)
  6. {
  7. Console.WriteLine("已推送说说标题为: " + log.Title + " 的说说给:" + item.QQ.ID);
  8. //投送 发往指定人
  9. }
  10. }

然后就是调用

  1. User user1 = , , new List<User>() { }));
  2. User user2 = , , new List<User>() { }));
  3. User user3 = , , new List<User>() { }));
  4. //new 一个用户 拥有一个QQ号和3个好友
  5. User currUser = , , new List<User>() { user1, user2, user3 }));
  6. Log temp = new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss"));
  7. //发说说
  8. currUser.PubLog(temp);
  9.  
  10. delegateEMS ems = new delegateEMS(PubEms);
  11. ems(currUser, temp);

然后就没有然后了 等着收货的给差评吧!

好吧说跑题了,继续回来说说QQ空间的事,刚才讲快递是为了加深大家对于委托的理解,我们用到了普通的委托有参无返回 但是发现每发一篇博客就要多写一次  大量重复的代码 我们接下来改为事件+委托

委托的进阶使用  事件+委托

事件可以说是无处不在 鼠标点击事件 移动事件就不说了  之所以用到事件就是为了实现触发器的效果  删除一条记录就邮件通知相关负责人 出现多少个警告也会通知 在我们项目中比较常用

基本语法

  1. public event PubLogHanler Handler; //这里事件类型是自定义的
  1. //定义推送委托
  2. public delegate void PubLogHanler(object o, PubLogHanlerArgs args);
  3. //发布日志触发
  4. public class PubLogHanlerArgs : EventArgs
  5. {
  6. public PubLogHanlerArgs(){}
  7. public PubLogHanlerArgs(Log log)
  8. {
  9. LogInfo = log;
  10. }
  11. public Log LogInfo { get; set; }
  12.  
  13. }

这里自定义了一个说说发布的事件,它继承EventArgs类,是任何事件的基类,触发一个事件时会记录下说说的信息

  1. public void PubLog(Log log)
  2. {
  3. Console.WriteLine("发布说说中...");
  4. StringBuilder sb = new StringBuilder();
  5. sb.AppendLine("====================================");
  6. sb.AppendLine(log.Title);
  7. sb.AppendLine(" "+log.Content);
  8. sb.Append("\t"+log.Author.ToString()+"\t");
  9. sb.AppendLine(log.PubTime);
  10. sb.AppendLine("====================================");
  11. Console.WriteLine(sb.ToString());
  12.  
  13. //触发事件
  14.  
  15. OnPubLog(this, new PubLogHanlerArgs(log));
  16.  
  17. }
  18. public void OnPubLog(object o, PubLogHanlerArgs args)
  19. {
  20. if (Handler != null)
  21. {
  22.  
  23. Handler(o, args);
  24. }
  25.  
  26. }

从上面的代码可以看到 调用发布说说方法后会触发事件,然后事件会去执行推送方法达到触发器的效果

  1. //推送事件
  2. static void currUser_Handler(object o, PubLogHanlerArgs args)
  3. {
  4. User user = o as User;
  5. if(user!=null)
  6. {
  7. foreach (var item in user.QQ.FriendList)
  8. {
  9. Console.WriteLine("已推送说说标题为: "+args.LogInfo.Title+" 的说说给:"+item.QQ.ID);
  10. //推送 存储日志编号到好友的待加载列表里面
  11. }
  12. }
  13.  
  14. }

其实上面只是个方法,只是参数看起来是事件罢了,接下来就是绑定事件

  1. //推送给好友
  2. currUser.Handler += new PubLogHanler(currUser_Handler);
  1. //发说说
  2. currUser.PubLog(new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));
  3.  
  4. currUser.PubLog(new Log("终于上博客园首页啦", "博客园是个学习的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));

事件的妙处就在于可以绑定多个事件 ,叫多播委托。再定义一个事件

  1. //已通知更新缓存
  2. static void UpdateCache_Handler(object o, EventArgs args)
  3. {
  4. User user = o as User;
  5. if (user != null)
  6. {
  7.  
  8. Console.WriteLine("\n<*>已通知更新缓存<*>\n");
  9. //已通知更新缓存
  10.  
  11. }
  12.  
  13. }

调用代码如下

  1. User user1 = , , new List<User>() { }));
  2. User user2 = , , new List<User>() { }));
  3. User user3 = , , new List<User>() { }));
  4. //new 一个用户 拥有一个QQ号和3个好友
  5. User currUser = , , new List<User>() { user1, user2, user3 }));
  6.  
  7. //推送给好友
  8. currUser.Handler += new PubLogHanler(currUser_Handler);
  9. //通知更新缓存
  10. currUser.Handler += new PubLogHanler(UpdateCache_Handler);
  11. //发说说
  12. currUser.PubLog(new Log("我的第一个说说", "说说开通了可以分享咯", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));
  13.  
  14. currUser.PubLog(new Log("终于上博客园首页啦", "博客园是个学习的好地方", currUser.QQ.ID, DateTime.Now.ToString("M.d HH:mm:ss")));
  15.  
  16. Console.ReadKey();

总结

委托和事件讲到这里也接近尾声了,我这也是临时抱佛脚,在网上看了写实例代码 自己实操了一遍就根据自己的理解发上来了。很多地方估计语句不是太通顺甚至概论都没理明白,如果有错误之处还望大家指正,免得误人子弟!

如果有需要源代码的话我稍后会发上来的。下一篇博客 我会讲一讲反射和线程,并且把我写的一个xml json datatable List 相互转换 的dll和代码分享给大家,请大家关注我的博客http://www.cnblogs.com/jingch/

源码

C#知识体系(二)用案例来理解委托与事件的更多相关文章

  1. 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门

    [详细][转]C#中理解委托和事件   文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...

  2. C#学习之初步理解委托、事件、匿名方法和Lambda

    最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对 ...

  3. 【前端知识体系-JS相关】深入理解JavaScript异步和单线程

    1. 为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Jav ...

  4. 【前端知识体系-JS相关】深入理解MVVM和VUE

    1. v-bind和v-model的区别? v-bind用来绑定数据和属性以及表达式,缩写为':' v-model使用在表单中,实现双向数据绑定的,在表单元素外使用不起作用 2. Vue 中三要素的是 ...

  5. 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链

    1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...

  6. C#基础篇 - 理解委托和事件

    1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...

  7. 【详细】【转】C#中理解委托和事件

    文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具 ...

  8. linux 运维知识体系

    这里将会介绍一下,LINUX运维工程师的知识体系. 只能说是个人理解吧.并不是必要或者充分的,仅供网友参考. 大部分本博客都有涉及,并不完整. 1.LINUX运维基础 1.1.LINUX系统的简介,分 ...

  9. c#关于委托和事件(二)(介绍的很详细)

    using System;using System.Collections.Generic;using System.Text; namespace Delegate {    // 热水器    p ...

随机推荐

  1. 转: Redis基础总结

    转文:http://blog.csdn.net/basycia/article/details/52175429 1.redis是什么 2.redis的作者何许人也 3.谁在使用redis 4.学会安 ...

  2. b/s 读取多个FTP文件(图片,视频)压缩到服务器 下载到客户端

    其实需求是这样, 要做一键导出, 有图片,有照片,youhtml,存在不同的文件夹,每次下载都必须下载最新数据,因为FTP是随时更新的. 1.这要是一直下载下载,浏览器一直跳窗口,蛋疼的我都看不下去. ...

  3. JS学习笔记--轮播图效果

    希望通过自己的学习收获哪怕收获一点点,进步一点点都是值得的,加油吧!!! 本章知识点:index this for if else 下边我分享下通过老师教的方式写的轮播图,基础知识实现: 1.css代 ...

  4. Android开发--TableLayout的应用

    1.简介 TableLayout为表格框架结构

  5. thinkjs中自定义sql语句

    一直以为在使用thinkjs时,只能是它自带的sql语句查询,当遇到类似于这样的sql语句时,却不知道这该怎样来写程序,殊不知原来thinkjs可以执行自定义sql语句 SELECT * from a ...

  6. 使用wait()与notify()实现线程间协作

    调用sleep()和yield()的时候锁并没有被释放,而调用wait()将释放锁.这样另一个任务(线程)可以获得当前对象的锁,从而进入它的synchronized方法中.可以通过notify()/n ...

  7. iocp 小例子

    2016-08-3116:44:09 server 端 /******************************************************************* aut ...

  8. 2014 NOIP 赛前自我整理提醒。

    空谈WA,实干AC. 所以作为一个就要上战场的OIer ,实干当然是最重要,但刷题不在多,要点牢记是关键,虽然本渣没记住多少,但还是列几点值得注意的小点. 1.战场上容不得失误. 对于每日都要敲键盘的 ...

  9. fonts.useso.com 访问变慢

    fonts.useso.com 替换为        fonts.lug.ustc.edu.cn ajax.useso.com   替换为       ajax.lug.ustc.edu.cn the ...

  10. Eclipse 项目中有红色感叹号,怎么办?

    /** * JDK1.5中增加了自动拆装箱的语言特性,在基本类型和包装类型之间可以相互地转换和运算. * 大家也都知道Java中==运算符是比较两个对象间的引用是否相同.在自动拆装箱与“==”运算符之 ...