• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > 详解C#编程中.NET的弱事件模式

详解C#编程中.NET的弱事件模式

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c语言32关键字详解,c语言链表详解,c语言指针详解,c语言题库及详解答案,c语言关键字详解等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

 引言

你可能知道,事件处理是内存泄漏的一个常见来源,它由不再使用的对象存留产生,你也许认为它们应该已经被回收了,但不是,并有充分的理由。

在这个短文中(期望如此),我会在 .Net 框架的上下文事件处理中展示这个问题,之后我会教你这个问题的标准解决方案,弱事件模式。有两种方法,即:

  •     “传统”方法 (嗯,在 .Net 4.5 前,所以也没那么老),它实现起来比较繁琐
  •     .Net 4.5 框架提供的新方法,它则是尽其可能的简单

(源代码在 这里 可供使用。)

从常见事物开始

在一头扎进本文核心内容前,让我们回顾一下在代码中最常使用的两个事物:类和方法。
事件源

让我为您介绍一个基本但很有用的事件源类,它最低限度地揭示了足够的复杂性来说明这一点:

 

public class EventSource
{
  public event EventHandlerEvent = delegate { };
 
  public void Raise()
  {
    Event(this, EventArgs.Empty);
  }
}
</div>

对好奇那个奇怪的空委托初始化方法(delegate { })的人来说,这是一个用来确保事件总被初始化的技巧,这样就可以不必每次在使用它之前都要检查它是否不为NULL。

触发垃圾收集的实用方法

在.net中,垃圾收集以一种不确定的方式触发。这对我们的实验很不利,我们的实验需要以一种确定的方式跟踪对象的状态。

所以,我们必须定期触发自己的垃圾收集操作,同时避免复制管道代码,管道代码已经在在一个特定的方法中释放:
 

static void TriggerGC()
{
  Console.WriteLine("Starting GC.");
 
  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
 
  Console.WriteLine("GC finished.");
}
</div>

虽然不是很复杂,但是如果你不是很熟悉这种模式,还是有必要小小解释一下:

  •     第一个 GC.Collect() 触发.net的CLR垃圾收集器,对于负责清理不再使用的对象,和那些类中没有终结器(即c#中的析构函数)的对象,CLR垃圾收集器足够胜任
  •     GC.WaitForPendingFinalizers() 等待其他对象的终结器执行;我们需要这样做,因为,你将看到我们使用终结器方法去追踪我们的对象在什么时候被收集的
  •     第二个GC.Collect() 确保新生成的对象也被清理了

引入问题

首先让我们试着通过一些理论,最重要的是还有一个演示的帮助,去了解事件监听器有哪些问题。
背景

一个对象要想被作为事件侦听器,需要将其实例方法之一登记为另一个能够产生事件的对象(即事件源)的事件处理程序,事件源必须保持一个到事件侦听器对象的引用,以便在事件发生时调用此侦听器的处理方法。

这很合理,但如果这个引用是一个 强引用,则侦听器会作为事件源的一个依赖 从而不能作为垃圾回收,即使引用它的最后一个对象是事件源。


下面详细图解在这下面发生了什么:

事件处理问题

这将不是一个问题,如果你可以控制listener object的生命周期,你可以取消对事件源的订阅当当你不再需要listener,常常可以使用disposable pattern(用后就扔的模式)。

但是如果你不能在listener生命周期内验证单点响应,在确定性的方式中你不能把它处理掉,你必须依赖GC处理...这将从不会考虑你所准备的对象,只要事件源还存在着!

例子

理论都是好的,但还是让我们看看问题和真正的代码。

这是我们勇敢的时间监听器,还有点幼稚,我们很快知道为什么:
 

public class NaiveEventListener
{
  private void OnEvent(object source, EventArgs args)
  {
    Console.WriteLine("EventListener received event.");
  }
 
  public NaiveEventListener(EventSource source)
  {
    source.Event += OnEvent;
  }
 
  ~NaiveEventListener()
  {
    Console.WriteLine("NaiveEventListener finalized.");
  }
}
</div>

用一个简单例子来看看怎么实现运作:
 

Console.WriteLine("=== Naive listener (bad) ===");
 
EventSource source = new EventSource();
 
NaiveEventListener listener = new NaiveEventListener(source);
 
source.Raise();
 
Console.WriteLine("Setting listener to null.");
listener = null;
 
TriggerGC();
 
source.Raise();
 
Console.WriteLine("Setting source to null.");
source = null;
 
TriggerGC();
</div>

输出:

 

EventListener received event.
Setting listener to null.
Starting GC.
GC finished.
EventListener received event.
Setting source to null.
Starting GC.
NaiveEventListener finalized.
GC finished.
</div>

让我们分析下这个运作流程:

  •     “EventListener received event.“:这是我们调用 “source.Raise()”的结果; perfect, seems like we're listening.
  •     “Setting listener to null.“: 我们把本地事件监听器对象引用赋空值,这样应该可以让垃圾回收器回收了.
  •     “Starting GC.“: 垃圾回收开始.
  •     “GC finished.“: 垃圾回收开始, 但是 但是我们的事件监听器没有被回收器回收, 这样就证明了事件监听器的析构函数没有被调用。
  •     “EventListener received event.“: 第二次调用 “source.Raise()”来确认,发现这监听器还活着。
  •     “Setting source to null.“: 我们在赋空值给事件的原对象.
  •     “Starting GC.“: 第二次垃圾回收.
  •     “NaiveEventListener finalized.“: 这一次幼稚的事件监听终于被回收了,迟到总好过没有.
  •     “GC finished.“:第二次垃圾回收完成.


结论:确实有一个隐藏的对事件监听器的强引用,目的是防止它在事件源被回收之前被回收!

希望有针对此问题的标准解决方案:让事件源可以通过弱引用来引用侦听器,在事件源存在时也可以回收侦听器对象。

这里有一个标准的模式及其在.NET框架上的实现:弱事件模式(http://msdn.microsoft.com/en-us/library/aa970850.aspx)。 And there is a standard pattern and its implementation in the .Net framework: the weak event pattern.

弱事件模式

让我们看看在.NET中如何应付这个问题,

通常有超过一种方法去做,但是在这种情况下可以直接决定:

  •     如果你正在使用 .Net 4.5 ,那么你将从简单的实现受益
  •     另外,你必须依靠一点人为的技巧手段

传统方式

  •     WeakEventManager 是所有模式管道的封装
  •     IWeakEventListener 是管道,它允许一个组件连接到WeakEventManager管件

(这两个位于WindowBase程序集,你将需要参考你自己的如果你不在开发WPF项目,你应该准确的参考WindowBase)


因此这有两步处理.

首先通过继承WeakEventManager来实现一个自定义事件管理器:

  •     重写 StartListening 和 StopListening 方法,分别注册一个新的handler和注销一个已存在的; 它们将被WeakEventManager基类使用。
  •     提供两个方法来访问listener列表, 命名为 “AddListener” 和 “RemoveListener “,给自定义事件管理器的使用者使用。
  •     通过在自定义事件管理器上暴露一个静态属性,提供一个方式去获得当前线程的事件管理器。
  • 之后使listenr实现IWeakEventListenr接口:
  •     实现 ReceiveWeakEvent 方法
  •     尝试去处理这个事件
  •     如果无误的处理好事件,将返回true


有很多要说的,但是可以相对地转换成一些代码:

首先是自定义弱事件管理器:
 

public class EventManager : WeakEventManager
{
  private static EventManager CurrentManager
  {
    get
    {
      EventManager manager = (EventManager)GetCurrentManager(typeof(EventManager));
 
      if (manager == null)
      {
        manager = new EventManager();
        SetCurrentManager(typeof(EventManager), manager);
   



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 详解C#中三个关键字params,Ref,out
  • 详解三种C#实现数组反转方式
  • 详解C# 利用反射根据类名创建类的实例对象
  • 详解C#用new和override来实现抽象类的重写区别
  • 详解C#把DataTable中数据一次插入数据库的方法
  • 详解c# 类的构造方法
  • 详解C#多线程之线程同步
  • 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿
  • 详解C#中的委托
  • 详解C#中的out和ref

相关文章

  • 2017-05-28C#实现的XML操作类实例
  • 2017-05-28C#实现彻底删除文件的方法
  • 2017-05-28Silverlight将图片转换为byte的实现代码
  • 2017-05-28基于C#生成随机数示例
  • 2017-05-28C#实现的调用DOS命令操作类实例
  • 2017-05-28C# dynamic关键字的使用方法
  • 2017-05-28C#程序提示“正由另一进程使用,因此该进程无法访问该文件”的解决办法
  • 2017-05-28C#中怎么将一个List转换为只读的
  • 2017-05-28C#中实现查找mysql的安装路径
  • 2017-05-28轻松学习C#的密封类

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C#生成随机数的方法小结
    • 基于C#中IDisposable与IEnumerable、IEnumerator的应用
    • C#中Html.RenderPartial与Html.RenderAction的区别分析
    • C#中观察者模式的3种实现方式
    • C#使用foreach语句搜索数组元素的方法
    • 超炫酷的WPF实现Loading控件效果
    • C#/Java连接sqlite与使用技巧
    • C# 中的EventHandler实例详解
    • C#面向对象特征的具体实现及作用详解
    • c#哈希算法的实现方法及思路

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有