C#笔记11多播委托、事件、设计模式(观察者模式)
文章目录
- 多播委托
- 初步理解多播委托为委托列表
- 报社发报案例
- 增加接口编程改善案例
- 再用委托类型对上例进行改善
- 委托链的异常
- 事件
- 提供封装,程序更健壮
- 提供更加抽象的事件用法
- 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多播委托、事件、设计模式(观察者模式)相关推荐
- java委托事件与观察者模式_多播委托与观察者模式联合使用,以及委托与事件的区别...
首先我们先看一下多播委托: 使用委托时,首先我们声明委托,委托语法一共有四种类型,分别时有参,无参,以及有无参数返回值. 1 public class DelegateShow //: System. ...
- c#_委托事件的理解
我找了三篇写的比较不错的博客,希望对委托和事件的理解有所帮助: 第一篇: 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上 ...
- 设计模式---观察者模式(Observer)和委托事件模型(DEM)
1 引言 观察者模式,又名发布订阅模式,是一个一对多的关系,当被观察者发生某种变化,对应其观察者做出相应的改变.比如说,某学校研究生实验室有2个学生,2个学生某个上午在实验室,A在玩游戏,B在看电影, ...
- c#事件的发布-订阅模型_C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命...
使用面向对象的思想 用c#控制台代码模拟猫抓老鼠 我们先来分析一下猫抓老鼠的过程 1.猫叫了 2.所有老鼠听到叫声,知道是哪只猫来了 3.老鼠们逃跑,边逃边喊:"xx猫来了,快跑啊!我是老鼠 ...
- 第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)
一. 委托的发展历史和基本用法 说起委托,每个人可能都会对他有不同的理解,结合实战中委托的使用,我对其理解是:委托和类一样,是用户的一个自定义类型,委托可以有参数.有返回值,委托的关键字是delega ...
- Observer设计模式中-委托事件-应用在消息在窗体上显示
Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...
- 【学习笔记】ABAP OOD设计模式 - 观察者模式
ABAP OOD设计模式 - 观察者模式 整理转自-<SAP ABAP 面向对象程序设计(原则.模式及实践)> 定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,这个对象相关依赖的 ...
- Unity C#笔记 委托事件
C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...
- Unity之C#——委托与事件,观察者模式,猫和老鼠事例
委托与事件,观察者模式,猫和老鼠事例 在Unity游戏开发中,我们经常需要在一个类中,调用另一个类中的方法,比如,当玩家进入到某个地方,敌人就开始攻击玩家.这时就需要利用委托与事件,设计观察者模式. ...
最新文章
- C++ 获得指定路径文件的“修改日期”
- 【枚举】数列(jzoj 1507)
- BASH 比较运算小结[转载 小蜗牛五二]
- Win10 JAVA安装及环境搭建(windows jdk,windows java环境配置)
- 【安全】通过LAM(ldap-account-manager)来管理OpenLDAP
- 互联网大厂春节礼盒鄙视链
- java 上调下移_java – 在调整框架大小时,JLabel的位置会更...
- Web前端学习-第一课JavaScript篇
- Python转换图片格式 -- PIL库的使用
- 【图像压缩】基于matlab GUI FFT图像压缩【含Matlab源码 843期】
- 环境影响评价期末考试题库
- 把照片的字转换为数字版
- 爬虫一:用正则表达式爬取图片
- 什么目录存放Linux源代码,Linux根目录下有哪些目录,存放的内容是什么?
- 照相馆、摄影工作室如何利用选片和底片下载来做微信公众号吸粉
- linux和主机共享文件,设置Linux虚拟机与主机共享文件的方法
- c语言程序电子琴21个音符,14键电子琴曲谱_小电子琴有14个键音符怎么标
- 英文版Windows XP的中文支持设置
- 自定义resttemplate的ErrorHandler
- 淘宝详情接口调用示例