.net4.5的弱事件
.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爽!!
.net4.5的弱事件的更多相关文章
- [AaronYang]C#人爱学不学8[事件和.net4.5的弱事件深入浅出]
没有伟大的愿望,就没有伟大的天才--Aaronyang的博客(www.ayjs.net)-www.8mi.me 1. 事件-我的讲法 老师常告诉我,事件是特殊的委托,为委托提供了一种发布/订阅机制. ...
- c#弱事件(weak event)
传统事件publisher和listener是直接相连的,这样会对垃圾回收带来一些问题,例如listener已经不引用任何对象但它仍然被publisher引用 垃圾回收器就不能回收listener所占 ...
- 【转载】详细解读C#中的 .NET 弱事件模式
你可能知道,事件处理是内存泄漏的一个常见来源,它由不再使用的对象存留产生,你也许认为它们应该已经被回收了,但不是,并有充分的理由. 在这个短文中(期望如此),我会在 .Net 框架的上下文事件处理中展 ...
- WPF 弱事件
因为在接触WPF的过程中追查INotifyPropertyChanged的通知原理的时候,发现了 PropertyChangedEventManager这个类,它是继承与WeakEventManage ...
- C#中的 .NET 弱事件模式
引言 你可能知道,事件处理是内存泄漏的一个常见来源,它由不再使用的对象存留产生,你也许认为它们应该已经被回收了,但不是,并有充分的理由. 在这个短文中(期望如此),我会在 .Net 框架的上下文事件处 ...
- 在WPF中应用弱事件模式
http://www.cnblogs.com/rickiedu/archive/2007/03/15/676021.html 在wpf中应用弱事件模式 感谢VS 的Intellisens ...
- WPF程序中的弱事件模式
在C#中,得益于强大的GC机制,使得我们开发程序变得非常简单,很多时候我们只需要管使用,而并不需要关心什么时候释放资源.但是,GC有的时并不是按照我们所期望的方式工作. 例如,我想实现一个在窗口的标题 ...
- 简单说说.Net中的弱引用
弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat kitty = new Cat(); 变量ki ...
- .NET中的弱引用
弱引用是什么? 要搞清楚什么是弱引用,我们需要先知道强引用是什么.强引用并不是什么深奥的概念,其实我们平时所使用的.Net引用就是强引用.例如: Cat cat = new Cat(); 变量cat就 ...
随机推荐
- 本文摘录 - Infobright
背景 论文 Brighthouse: AnAnalytic Data Warehouse for Ad-hoc Queries.VLDB 2008 brighthouse它是一个面向列的数据仓库.在数 ...
- iOS开发无第三方控件的援助达到的效果侧边栏
最近的研究iOS程序侧边栏.渐渐的发现iOS该方案还开始采取风侧边栏格该,QQ,今日头条,Path(Path运营商最早的侧边栏app该,效果说成是Path效果),所以就研究了下. 然后发现Git Hu ...
- 跟着辛星认识一下PHP的自己主动载入
作为一个框架,文件的载入机制是不能少的,那么我们应该怎么载入呢,这些PHP已经给我们想好了,所以我们仅仅须要依照规则办事就能够了,PHP中有两个函数能够完毕这个功能,第一个是__autoload,如今 ...
- android代码签名和混乱的包装
研究了一下android的apk困惑签名和代码包装,假设没有混乱包.然后apk人们可以直接查看源代码反编译出来,尽管混乱包或能看懂.但不是那么容易理解,要求在至少一些时间 假设不混淆,反编译后的代码例 ...
- SQL入门学习6-集合运算
7-1 表的加减法 集合运算 集合运算就是对满足同一规则的记录,进行的加减等四则运算. 1.1 表的加法--UNION 表之间进行并集运算. 语法: SELECT 对应列1,对应列2-- FROM 表 ...
- SQL开发中容易忽视的一些小地方(五)
原文:SQL开发中容易忽视的一些小地方(五) 背景: 索引分类:众所周知,索引分为聚集索引和非聚集索引. 索引优点:加速数据查询. 问题:然而我们真的清楚索引的应用吗?你写的查询语句是否能充分应用上索 ...
- JUnit实战(2) - JUnit核心(使用Suite来组合测试)
创建Java Project项目:ch02-internals MasterTestSuite.java package com.manning.junitbook.ch02.internals; i ...
- Dynamic proxy
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflec ...
- tomcat内存溢出,改动设置
问题描写叙述: 1. java.lang.OutOfMemoryError: Java heap space JVM堆的设置是指java程序执行过程中JVM能够调配使用的内存空间的设置.JVM在启动的 ...
- 对“demo!demo.Index+HookProc::Invoke”垃圾收集的类型已委托回调。这可能会导致应用程序崩溃、损坏和数据丢失。当传递委托给非托管代码,托管应用程序必须让这些委托保持活着
对"demo!demo.Index+HookProc::Invoke"垃圾收集的类型已委托回调.这可能会导致应用程序崩溃.损坏和数据丢失.当传递委托给非托管代码,托管应用程序必须承 ...