文章目录

  • 多播委托
    • 初步理解多播委托为委托列表
    • 报社发报案例
    • 增加接口编程改善案例
    • 再用委托类型对上例进行改善
    • 委托链的异常
  • 事件
    • 提供封装,程序更健壮
    • 提供更加抽象的事件用法
    • System.EventArgs传入参数
    • 使用系统预定义的泛型委托
      • add、remove关键字

多播委托

初步理解多播委托为委托列表

static Action<int> all;
static Action<int> AddThenPrint = i => { i++; Console.WriteLine(i);};
static Action<int> Print = i => Console.WriteLine(i);...Main(..){all = AddThenPrint;all(1); //输出2all += Print; //此时all相当于[AddThenPrint, Print]all(1);  //输出2 1
}

多播委托,可以理解为一个列表,只要其中元素函数签名一致,就可以记录在一起。多播委托就是多个委托的列表。

报社发报案例

class Person{public string Name {get; set;}public Person(string name){this.Name = name;}public void SetNewspaper(Newspaper newspaper){this.Newspaper = newspaper;}
}class Publisher{public string Name {get; set;}public Person(string name){this.Name = name;}public void SendNewspaper(Newspaper newspaper){var p1 = new Person("a");var p2 = new Person("b");var p3 = new Person("c");p1.SetNewspaper(newspaper);p2.SetNewspaper(newspaper);p3.SetNewspaper(newspaper);}
}class Newspaper{public string Title {get; set;}public string Content {get; set;}
}class Program{...Main(..){var publisher = new Publisher("出版社X");publisher.SendNewspaper(new Newspaper() {Title = "标题", Content = "内容"});}
}
  • 上述代码存在问题,代码不封闭。即只能把Newspaper Send给三个人,增加Person的话,需要去改Publisher中的成员函数。
  • 于是使用列表List<Person>,在Publish增加Persons成员。
class Publisher{public string Name {get; set;}public Person(string name){this.Name = name;}public List<Person> Persons = new List<Person>();public void SendNewspaper(Newspaper newspaper){Persons.ForEach(person => person.SetNewspaper(newspaper));}
}class Program{...Main(..){var publisher = new Publisher("出版社X");publish.Persons.Add(new Person("a"));publish.Persons.Add(new Person("b"));publish.Persons.Add(new Person("c"));publisher.SendNewspaper(new Newspaper() {Title = "标题", Content = "内容"});}
}
  • 假设不仅仅是Person需要收报纸;Company也需要收报纸。
  • 增加Company类如下。
class Company{public string Name {get; set;}public Company(string name){this.Name = name;}public void SetNewspaper(Newspaper newspaper){this.Newspaper = newspaper;}
}
  • 但是增加了公司这类,Publisher又不封闭了。需要增加两行代码(增加一个公司列表并遍历)如下。
public List<Person> Persons = new List<Person>();
public List<Company> Companies= new List<Company>();public void SendNewspaper(Newspaper newspaper){Persons.ForEach(person => person.SetNewspaper(newspaper));
Companies.ForEach(c=> c.SetNewspaper(newspaper));}
}
  • 于是引用面向接口编程。
  • 将公共操作统一到接口。

增加接口编程改善案例

interface INewspaper{void SetNewspaper(Newspaper newspaper);void ReadNwspaper();
}class Person : INewspaper{public string Name {get; set;}public Person(string name){this.Name = name;}public void SetNewspaper(Newspaper newspaper){this.Newspaper = newspaper;}
}class Company : INewspaper{public string Name {get; set;}public Company(string name){this.Name = name;}public void SetNewspaper(Newspaper newspaper){this.Newspaper = newspaper;}
}class Publisher{public string Name {get; set;}public Person(string name){this.Name = name;}public List<INewspaper> Subscribers= new List<INewspaper>();// 通过接口调用实现接口的类public void SendNewspaper(Newspaper newspaper){Subscribers.ForEach(subscriber => subscriber.SetNewspaper(newspaper));}
}
  • 上述例中通过接口调用实现接口的类,封装了Publisher代码;
  • 上述例实现了简单的观察者模式(又名 订阅/发布模式)。
  • 但是使用C#自己的方式实现更简单,修改为委托类型。

再用委托类型对上例进行改善

  • 通过委托把接口定义去掉。
//去掉接口class Publisher{public string Name {get; set;}public Person(string name){this.Name = name;}// public List<INewspaper> Subscribers= new List<INewspaper>();// 使用委托public delegate void _Subscribers(Newspaper newspaper);// 委托是定义一个类,再实现public _Subscribers Subscribers{ get; set; }public void SendNewspaper(Newspaper newspaper){Subscribers(newspaper);}
}class Program{...Main(..){var publisher = new Publisher("出版社X");Person A = new Person("A");Company B = new Company("B");publisher.Subscribers = A.SetNewspaper;publisher.Subscribers += B.SetNewspaper;//使用了多播委托publisher.SendNewspaper(new Newspaper() {Title = "标题", Content = "内容"});}
}
  • 还可以用Action化简delegate如下。
//去掉接口class Publisher{public string Name {get; set;}public Person(string name){this.Name = name;}// public List<INewspaper> Subscribers= new List<INewspaper>();// 使用委托// public delegate void _Subscribers(Newspaper newspaper);// 委托是定义一个类,再实现// public _Subscribers Subscribers{ get; set; }public Action<Newspaper> Subscribers = null; // 默认应该为nullpublic void SendNewspaper(Newspaper newspaper){Subscribers(newspaper);}
}
  • 上述程序还是有一些bug的,比如没有坚持null。
class Publisher{...public void SendNewspaper(Newspaper newspaper){if(Subscribers != null){Subscribers(newspaper);}}
}

委托链的异常

  • 委托实现时一口气都处理掉,为了调出出异常的委托链中的部分,使用如下技巧。
  • 使用GetInvocationList()方法。
class Publisher{...public void SendNewspaper(Newspaper newspaper){if(Subscribers != null){// 检测委托链中的异常foreach(var handler in Subscribers.GetInvocationList()){try{handler(newspaper);}catch(Exception ex){Console.WriteLine(ex.Message);}}Subscribers(newspaper);}}
}

事件

  • 误将将+=写成=,出bug,程序不健壮。
  • 引入event关键字,提供两个额外封装,提供更加抽象的事件用法。

提供封装,程序更健壮

class Publisher{...public event Action<Newspaper> Subscribers = null;// 因为Action为系统已经声明的delegate,因此无需声明。// 如果是自己声明的委托需要如下两步骤。public delegate void XX(Newspaper n);public event XX xx;public void SendNewspaper(Newspaper newspaper){if(Subscribers != null){// 检测委托链中的异常foreach(Action<Newspaper> handler in Subscribers.GetInvocationList()){try{handler(newspaper);}catch(Exception ex){Console.WriteLine(ex.Message);}}Subscribers(newspaper);}}
}
  • 对于事件,有两大好处:
    • 只能用 += 、 -=(程序更为健壮);
    • 外部不能直接调用委托,只能在类的内部调用,如下。
publisher.Subscribers(null); // 外部不能直接调用event委托

提供更加抽象的事件用法

  • 下例中,有两个出版商,如果将出版商信息传入。
  • 在参数设置时增加一个object,将类本身传入。
class Publisher1{...public event Action<object, Newspaper> Subscribers = null;// 因为Action为系统已经声明的delegate,因此无需声明。// 如果是自己声明的委托需要如下两步骤。public delegate void XX(Newspaper n);public event XX xx;public void SendNewspaper(Newspaper newspaper){if(Subscribers != null){// 检测委托链中的异常foreach(Action<object, Newspaper> handler in Subscribers.GetInvocationList()){try{// 加入thishandler(this, newspaper);}catch(Exception ex){Console.WriteLine(ex.Message);}}Subscribers(newspaper);}}
}
  • 因此也需要更改Person中的SetNewspaper()方法,加入object sender参数。
class Person{public string Name {get; set;}public Person(string name){this.Name = name;}public void SetNewspaper(object sender, Newspaper newspaper){if(sender is Publisher1){newspaper.PublisherName = (sender as Publisher1).Name;}...this.Newspaper = newspaper;}
}
  • 更改Company同理。

System.EventArgs传入参数

  • 根据需要,还可以加一个参数类如下。
class PublisherArgs : System.EventArgs{public PublisherArgs(Newspaper newspaper){this.Newspaper = newspaper;}public Newspaper Newspaper{ get; set; }
}
  • 之后即可更改传入值为PublisherArgs()。
  • 别的签名也要跟着换。
class Publisher1{...public event Action<object, PublisherArgs> Subscribers = null;public delegate void XX(Newspaper n);public event XX xx;public void SendNewspaper(Newspaper newspaper){if(Subscribers != null){foreach(Action<object, Newspaper> handler in Subscribers.GetInvocationList()){try{// 这里改为new PublisherArgs()handler(this, new PublisherArgs(newspaper));}catch(Exception ex){Console.WriteLine(ex.Message);}}Subscribers(newspaper);}}
}
  • Person的相关签名也要改。
  • 在Winform中比较常用(object sender, Args arg)。
class Person{public string Name {get; set;}public Person(string name){this.Name = name;}public void SetNewspaper(object sender, PublisherArgs args){if (sender is Publisher1){agrs.Newspaper.PublisherName = (sender as Publisher1).Name;// 多了一层封装 args.Newspaper...}}
}
// 许多地方签名要改为args.Newspaper

使用系统预定义的泛型委托

public delegate void EventHandler<T>(object sender, T e)where T : EventArgs;
  • 委托也可以加约束。
add、remove关键字
public event EventHandler<PublisherArgs> Subscribers{add{ //.. }remove{ //.. }// 与+= 、-=效果类似
}

C#笔记11多播委托、事件、设计模式(观察者模式)相关推荐

  1. java委托事件与观察者模式_多播委托与观察者模式联合使用,以及委托与事件的区别...

    首先我们先看一下多播委托: 使用委托时,首先我们声明委托,委托语法一共有四种类型,分别时有参,无参,以及有无参数返回值. 1 public class DelegateShow //: System. ...

  2. c#_委托事件的理解

    我找了三篇写的比较不错的博客,希望对委托和事件的理解有所帮助: 第一篇: 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上 ...

  3. 设计模式---观察者模式(Observer)和委托事件模型(DEM)

    1 引言 观察者模式,又名发布订阅模式,是一个一对多的关系,当被观察者发生某种变化,对应其观察者做出相应的改变.比如说,某学校研究生实验室有2个学生,2个学生某个上午在实验室,A在玩游戏,B在看电影, ...

  4. c#事件的发布-订阅模型_C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命...

    使用面向对象的思想 用c#控制台代码模拟猫抓老鼠 我们先来分析一下猫抓老鼠的过程 1.猫叫了 2.所有老鼠听到叫声,知道是哪只猫来了 3.老鼠们逃跑,边逃边喊:"xx猫来了,快跑啊!我是老鼠 ...

  5. 第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)

    一. 委托的发展历史和基本用法 说起委托,每个人可能都会对他有不同的理解,结合实战中委托的使用,我对其理解是:委托和类一样,是用户的一个自定义类型,委托可以有参数.有返回值,委托的关键字是delega ...

  6. Observer设计模式中-委托事件-应用在消息在窗体上显示

    Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...

  7. 【学习笔记】ABAP OOD设计模式 - 观察者模式

    ABAP OOD设计模式 - 观察者模式 整理转自-<SAP ABAP 面向对象程序设计(原则.模式及实践)> 定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,这个对象相关依赖的 ...

  8. Unity C#笔记 委托事件

    C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...

  9. Unity之C#——委托与事件,观察者模式,猫和老鼠事例

    委托与事件,观察者模式,猫和老鼠事例 在Unity游戏开发中,我们经常需要在一个类中,调用另一个类中的方法,比如,当玩家进入到某个地方,敌人就开始攻击玩家.这时就需要利用委托与事件,设计观察者模式. ...

最新文章

  1. C++ 获得指定路径文件的“修改日期”
  2. 【枚举】数列(jzoj 1507)
  3. BASH 比较运算小结[转载 小蜗牛五二]
  4. Win10 JAVA安装及环境搭建(windows jdk,windows java环境配置)
  5. 【安全】通过LAM(ldap-account-manager)来管理OpenLDAP
  6. 互联网大厂春节礼盒鄙视链
  7. java 上调下移_java – 在调整框架大小时,JLabel的位置会更...
  8. Web前端学习-第一课JavaScript篇
  9. Python转换图片格式 -- PIL库的使用
  10. 【图像压缩】基于matlab GUI FFT图像压缩【含Matlab源码 843期】
  11. 环境影响评价期末考试题库
  12. 把照片的字转换为数字版
  13. 爬虫一:用正则表达式爬取图片
  14. 什么目录存放Linux源代码,Linux根目录下有哪些目录,存放的内容是什么?
  15. 照相馆、摄影工作室如何利用选片和底片下载来做微信公众号吸粉
  16. linux和主机共享文件,设置Linux虚拟机与主机共享文件的方法
  17. c语言程序电子琴21个音符,14键电子琴曲谱_小电子琴有14个键音符怎么标
  18. 英文版Windows XP的中文支持设置
  19. 自定义resttemplate的ErrorHandler
  20. 淘宝详情接口调用示例

热门文章

  1. 【Hive】动态分区插入
  2. 关于vmware虚拟机硬件里没有软盘驱动器,而操作系统里还有的解决方法
  3. datatable行内内容太长,有时不自动换行解决方法
  4. jmeter(二十二)内存溢出原因及解决方法
  5. swift中的let和var有什么区别?
  6. Windows命令提示符中的别名
  7. 如何在MySQL中重置AUTO_INCREMENT?
  8. Win10怎么让英伟达独立显卡成为主显卡
  9. activiti删除已经部署的流程定义
  10. java redis事务_Redis事务