[AaronYang]C#人爱学不学8[事件和.net4.5的弱事件深入浅出]
没有伟大的愿望,就没有伟大的天才--Aaronyang的博客(www.ayjs.net)-www.8mi.me
1. 事件-我的讲法
老师常告诉我,事件是特殊的委托,为委托提供了一种发布/订阅机制。
- 自定义事件:自定义一个类,继承EventArgs
- 使用泛型委托EventHandler<T>,本质:public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e) where TEventHandlers:EventArgs
- 定义事件的简单写法 public event EventHandler<TEventArgs> myevent;这里的EventHandler<TEventArgs>指的是符合2中的方法签名形式,void,有两个参数的,第一个sender参数包含事件的发送者。第二个参数提供了事件的相关信息。因为这个事件可以自定义,所以可以带来很多丰富的信息,也可以使用系统自带的。
- 使用事件属性中的add和remove添加或者删除委托的处理程序(事件),也可以自己使用+=或者-=处理
- 使用事件
1.1 我们来写一个简单的例子,让你理解这几个知识。
需求:我需要用户使用我提供的类,可能就是个dll了,可以在自己的类中,定义事件的详细内容。例如:博客事件,有了1篇新文章,可能会有阅读者去阅读博客,有的人使用电脑,有的人使用手机,有的人使用kindle
设计:
第一步:自定义事件,好处是可以在触发事件时候提供其他的对象值
- public class BlogEventArgs : EventArgs {
- /// <summary>
- /// 博客标题
- /// </summary>
- /// <param name="title"></param>
- public BlogEventArgs(string title) {
- this.Title = title;
- }
- //博客标题
- public string Title { get; set; }
- }
第二步:定义个事件,方便用户增加 自己的定义的事件,删除自己的定义的事件,一个调用用户绑定好的事件(特殊的委托)的方法
- public class BlogEventProvider
- {
- /// <summary>
- /// 第一步:定义个事件,并封装,给用户绑定这个事件
- /// </summary>
- private event EventHandler<BlogEventArgs> blogEvents;
- public event EventHandler<BlogEventArgs> BlogEvents
- {
- add
- {
- blogEvents += value;
- }
- remove
- {
- blogEvents -= value;
- }
- }
- //触发用户绑定的事件,或者事件前加些处理,然后再触发
- public void PreviewReadBlog(string title) {
- Console.WriteLine("正在获取博客《{0}》的内容...",title);
- OnReadBlog(title);
- }
- protected virtual void OnReadBlog(string title) {
- if (blogEvents != null) {
- blogEvents(this, new BlogEventArgs(title));
- }
- }
- }
第三步:定义个博客支持的设备枚举
- /// <summary>
- /// 博客文章支持的设备
- /// </summary>
- public enum BlogSupportDevice
- {
- PC,
- Kindle,
- Mobile
- }
第四步:定义阅读博客的人,当然这里的事件的实现,也可以写在别处。
- /// <summary>
- /// 博客阅读者
- /// </summary>
- public class BlogReader
- {
- public BlogReader(string readername, BlogSupportDevice device) {
- this.ReaderName = readername;
- this.Device = device;
- }
- public string ReaderName { get; set; }
- public BlogSupportDevice Device { get; set; }
- /// <summary>
- /// 因为需要知道博客的标题还有其他细节,所以自定义了一个BlogEventArgs类
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- public void OnReadBlog(object sender,BlogEventArgs e) {
- Console.WriteLine("{3}:{0}使用了{1}阅读了《{2}》",ReaderName,Device.ToString(),e.Title,DateTime.Now.ToLocalTime());
- }
- }
第五步:使用
第六步:拓展与反思
1. 如果我绑定了N次相同的事件,然后执行事件,是只会触发1次,还是触发N次?
答案:是N次,这也是上篇文章留的题目,委托被绑定多个相同的方法会执行多次吗?
2. 完整第五步代码,使用-=移除上一个事件,不然执行事件时候,会执行多次,因为事件是特殊的委托,具有多播委托的特性。
- static void Main(string[] args)
- {
- //定义了事件提供者
- BlogEventProvider provider = new BlogEventProvider();
- //定义博客阅读人
- BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);
- //ay开始绑定自己定义的事件,事件提供者,也在触发这个事件之前加了一些措施,而不是任由用户自己搞的
- provider.BlogEvents += ay.OnReadBlog;
- //开始阅读博客
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[2依赖属性]");
- provider.BlogEvents -= ay.OnReadBlog;//移除ay阅读者
- //定义博客阅读人
- BlogReader yy = new BlogReader("杨洋", BlogSupportDevice.Kindle);
- //ay开始绑定自己定义的事件,事件提供者,也在触发这个事件之前加了一些措施,而不是任由用户自己搞的
- provider.BlogEvents += yy.OnReadBlog;
- //开始阅读
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[1布局]");
- Console.ReadLine();
- }
效果图:
2. 事件不复杂,就好比js中把方法当做一个参数传递,例如 function A(B,d){B()}
这里B是一个方法,人家调用A时候就可以自定义一个B,而B只是一个function对象,具有几个参数,在A函数的参数中未体现,但是在调用B时候就有体现了。假设B函数具有2个参数(x,y),那么
function A(B,d){ var e=B(1,3)}
function cusB(x,y){return x*y}
A(cusB,'自定义')
说白了,感觉有点像给用户留事件接口
===============aaronyang========www.8mi.me==================
2.弱事件-引入
理解事件的发布和订阅模型-publisher和 listener
aaronyang大话讲解:publisher就是定义事件的地方,如上例就是BlogEventProvider类,它里面定义了private event EventHandler<BlogEventArgs> blogEvents;
而Listener就是给这个事件 添加或者删除具体事件实现的地方。如上例就是BlogReader类,它里面有blogEvents符合事件(blogEvents)的方法签名。因此就可以正常用+=和-=委托的东东了。
OK,下面我们来测试为什么需要弱事件?对的,就是防止事件的内存泄露问题。
接下来,我们在Program类中加一个垃圾回收方法,方便测试
- static void TriggerGC()
- {
- Console.WriteLine("Starting GC.");
- GC.Collect();
- GC.WaitForPendingFinalizers();
- GC.Collect();
- Console.WriteLine("GC finished.");
- }
第一个 GC.Collect() 触发.net的CLR垃圾收集器,对于负责清理不再使用的对象,和那些类中没有终结器(即c#中的析构函数)的对象,CLR垃圾收集器足够胜任
GC.WaitForPendingFinalizers() 等待其他对象的终结器执行;
第二个GC.Collect() 确保新生成的对象也被清理了
接下来,我们接着上一个例子继续写,上面第五步的代码假设你有了。
我们现在把ay对象置空,接着让垃圾回收ay对象,然后我们执行事件
- static void Main(string[] args)
- {
- //定义了事件提供者
- BlogEventProvider provider = new BlogEventProvider();
- //定义博客阅读人
- BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);
- //ay开始绑定自己定义的事件,事件提供者,也在触发这个事件之前加了一些措施,而不是任由用户自己搞的
- provider.BlogEvents += ay.OnReadBlog;
- //开始阅读博客
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[2依赖属性]");
- //provider.BlogEvents -= ay.OnReadBlog;//移除ay阅读者
- ////定义博客阅读人
- //BlogReader yy = new BlogReader("杨洋", BlogSupportDevice.Kindle);
- ////ay开始绑定自己定义的事件,事件提供者,也在触发这个事件之前加了一些措施,而不是任由用户自己搞的
- //provider.BlogEvents += yy.OnReadBlog;
- ////开始阅读
- //provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[1布局]");
- //测试事件的强引用 ay
- ay = null;
- TriggerGC();
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[3高级事件]");
- Console.ReadLine();
- }
结果:
结果惊讶的发现:事件还能被执行!这就说明ay对象还在啊,这就是强引用,所以好多C#写代码都在执行完了事件后,就接着写其他的代码了,也不管垃圾回收能不能回收,导致了内存泄露的问题。
接着,我们再在 BlogEventProvider中增加一个析构函数,确认BlogEventProvider对象是否真的被回收
- ~BlogEventProvider(){
- Console.WriteLine("BlogEventProvider的析构函数被执行");
- }
OK,我们再在Program的Main方法中增加代码:
- provider = null;
- TriggerGC();
效果:
OK,我确定此时ay对象被释放了。当然不一定要去释放ay对象才能回收内存啊,事件的 内存泄露问题,你只要+=后,执行完,手动去-=事件就行了。这样,垃圾回收器在后台也就可以自动回收ay对象了,不然,你一直在使用provider对象时候,垃圾回收永远都不能回收ay,假设provider中事件绑定了很多事件实现,而这些实现,你执行了,但是都没有做-=,那么就会导致能存泄露的问题,当然也就是内存高占用的问题。好吧,假如你想再new一个provider,那个废弃,那你new的太多的话,也会很多内存,而你如果没有手动GC,那内存占用更恐怖。
- provider.BlogEvents -= ay.OnReadBlog;
- ay = null;
- TriggerGC();
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[3高级事件]");
那么这个问题,怎么解决?.net4.5提供了一个更直接的方法,弱引用,当然4.5以前也有弱引用的其他方式,当然比较复杂,这里不讲了,只讲4.5的方式。
3.弱事件-aaronyang开讲
如果用4.5的泛型模式,则写法超简单,再也不用继承WeakEventManager,IWeakEventListener了。MSDN位置:查看
而且再也不用担心绑定多次同一个事件,被执行多次了
我们使用
- System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);
替代刚才的
- provider.BlogEvents += ay.OnReadBlog;
前提:你要引入WindowsBase.dll
- static void Main(string[] args)
- {
- //定义了事件提供者
- BlogEventProvider provider = new BlogEventProvider();
- //定义博客阅读人
- BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);
- //ay开始绑定自己定义的事件,事件提供者,也在触发这个事件之前加了一些措施,而不是任由用户自己搞的
- //provider.BlogEvents += ay.OnReadBlog;
- System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);
- //测试绑定多次,也只会执行一次
- //System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);
- //开始阅读博客
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[2依赖属性]");
- ay = null;
- TriggerGC();
- provider.PreviewReadBlog("[Aaronyang] 写给自己的WPF4.5 笔记[3高级事件]");
- Console.ReadLine();
- }
效果:
发现第二次执行事件时候,ay已经被释放了。真TM爽!!
======安徽六安=========www.ayjs.net==========aaronyang========杨洋========www.8mi.me==========
[AaronYang]C#人爱学不学8[事件和.net4.5的弱事件深入浅出]的更多相关文章
- [AaronYang]C#人爱学不学[1]
当前编写时间:2014年12月24日21:11:14 编写人:杨洋(Aaronyang) 新文章:[AaronYang]C#人爱学不学[1] 声明:->可以理解为 联想到,或者关联的意思. ...
- [AaronYang]C#人爱学不学[7]
做一个决定,并不难,难的是付诸行动,并且坚持到底 --Aaronyang的博客(www.ayjs.net)-www.8mi.me 1. 委托-我的总结 1.1 委托:面试我都会说,把方法当参数.委托包 ...
- [AaronYang]C#人爱学不学[3]
本文章不适合入门,只适合有一定基础的人看.我更相信知识细节见高低,我是从4.0开始学的,终于有时间系统的学习C#5.0,是5.0中的知识,会特殊标记下.但写的内容也可能含有其他版本framework的 ...
- [AaronYang]C#人爱学不学[6]
不要回头,不要将就,做到这两点,人生就会简单很多幸福很多 --Aaronyang的博客(www.ayjs.net)-www.8mi.me 1. 运算符,还有哪些你能学到? 1.1 不安全运算符: si ...
- [AaronYang]C#人爱学不学[4]
本文章不适合入门,只适合有一定基础的人看.我更相信知识细节见高低,我是从4.0开始学的,终于有时间系统的学习C#5.0,是5.0中的知识,会特殊标记下.但写的内容也可能含有其他版本framework的 ...
- [AaronYang]C#人爱学不学[5]
这世上有三样东西是别人抢不走的:一是吃进胃里的食物,二是藏在心中的梦想,三是读进大脑的书 --Aaronyang的博客(www.ayjs.net) 1. 数组-的疑惑? 1.1 多维数组 ...
- [AaronYang]C#人爱学不学[2]
1. 记事本写C#,脱离vs 新建记事本,名字为 helloworld.cs using System; namespace Hello{ public class HelloWorldSay{ st ...
- C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享
原文:C# 桌面软件开发-深入学习 [1]- AY-C#人爱学不学-aaronyang技术分享 曾经我做office,不想依赖别人dll,就使用了 Type.GetTypeFromProgID 可以根 ...
- AY写给国人的教程- VS2017 Live Unit Testing[1/2]-C#人爱学不学-aaronyang技术分享
原文:AY写给国人的教程- VS2017 Live Unit Testing[1/2]-C#人爱学不学-aaronyang技术分享 谢谢大家观看-AY的 VS2017推广系列 Live Unit Te ...
随机推荐
- nginx 做负载均衡
最近正在研究Nginx,Nginx作为反向代理服务器,可以对Web服务器提供加速,并且具有负载均衡的功能. 首先我要在官网下载Nginx(http://nginx.org/en/download.ht ...
- java加密算法--MD5加密和哈希散列带秘钥加密算法源码
package com.ompa.common.utils; import java.security.MessageDigest; import java.security.NoSuchAlgori ...
- jenkins maven svn 部署web项目到本地Tomcat
查了N多网页,折腾了几个小时,终于部署成功,部署的过程比较坎坷,遇到各种问题,记录一下,不管大家是否会遇到的同样的问题,希望有所帮助: 常规操作: 1.下载jenkins,必须要做的一步,http:/ ...
- Linux gcc命令
一.简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objectiv ...
- Hive Word count
--https://github.com/slimandslam/pig-hive-wordcount/blob/master/wordcount.hql DROP TABLE myinput; DR ...
- ZooKeeper系列2:ZooKeeper的运行
问题导读1.如何启动ZooKeeper 服务?2.如何启动集群 1)单机模式 用户可以通过下面的命令来启动 ZooKeeper 服务: zkServer.sh start 复制代码 这个命令默认情况下 ...
- 怎么通过js获取上传的图片信息(临时保存路径,名称,大小)然后通过ajax传递给后端?
今天在论坛上看到这样一个问题,有必要编辑搜集下. 问题描述:怎么通过js获取上传的图片信息(临时保存路径,名称,大小)然后通过ajax传递给后端 题主用jquery接收 <input name= ...
- 抓取天猫和淘宝的详情页图片|Golang
taobao.go package main import ( "crypto/md5" "encoding/hex" "fmt" &quo ...
- Android中Adapter之BaseAdapter使用
Android界面中有时候需要显示稍微复杂的界面时,就需要我们自定义一个adapter,而此adapter就要继承BaseAdapter,重新其中的方法. Android中Adapte ...
- jQuery Ajax 操作函数及deferred对象
jQuery Ajax 操作函数 jQuery 库拥有完整的 Ajax 兼容套件.其中的函数和方法允许我们在不刷新浏览器的情况下从服务器加载数据. 函数 描述 jQuery.ajax() 执行异步 H ...