写在前面:

之前的过开发程中,我愈发觉得面对复杂的界面要求,最好还是用UserControl将不同模块的界面设计单独封装,以应对客户频繁地需求更改。这样做能够在面对对不同的UI要求时,动态的加载预先设计好的特定模块的UserControl,不需要用代码对界面进行复杂的控制,否则要用代码控制一个个控件的生成与显示。设计之初费力,后面维护起来比较方便。

背景介绍:

最近开发新工具,针对不同的模块的数据展示我设计了不同的布局单独封装为UserControl,放置在PanelControl中作为数据展示。为了能够灵活的进行数据初始化,我给每个UserControl都订阅了主程序的通知事件。使用委托回调的方式,主程序中调用委托,子控件控件中自动执行具体的初始化方法。

//定义一个带有一个参数的无返回值的委托
delegate void DelegateUpdateUserControl(DeviceConfig item);
//声明委托对象
private DelegateUpdateUserControl UpdateUserControl;

大事件发生:

在测试过程中,数据的动态加载以及UserControl的切换展示没有问题。但随着切换次数的增加,界面变得越来越卡顿。

此时的我心中顿时警铃大作——有些该被释放掉的资源没有被释放掉?!但是每次进行界面切换,我都会调用Clear方法,将PanelControl里面的对象清空(PanelControl.Clear();),这时我开始怀疑UserControl对象仍在被主程序引用,系统无法进行GC.Collect(),对象没有被回收。

测试:

我在UserControl中的订阅方法里增加一行Console.WriteLine方法,进行测试。

理想情况下每切换一次UserControl,会调用一次委托,让UserControl绑定的委托方法输出一条信息。测试却发现,第一次会输出1条、第二次输出2条、第三次输出3条...

这说明了除了第一次调用委托方法,后面每次调用都有不止一个对象在响应且数量依次增加,看来确实是每次的UserControl都没有被成功回收。

结合.NET的垃圾回收机制,断定UserControl对象还在被主程序引用,导致没有被成功释放,可鞥是没有解除委托与方法的绑定造成的。

我将针对这个问题做了以下优化:

修改以下代码:

//在切换控件时,先清空原先的旧UserControl
//清空PanelControl所有控件,通常情况下只存在一个控件
PanelControlContainer.Controls.Clear();

改为:

if (PanelControlContainer.Controls.Count > 0)
{
//获取到UserControl(PanelControlContainer)对象
UControlProperty control =(UControlProperty)PanelControlContainer.Controls[0];
//取消事件挂载,解除委托绑定
UpdateUserControl -= control.UpdateControls;
//从PanelControl中移除旧UserControl
PanelControlContainer.Controls.Remove(control);
//主动释放资源
control.Dispose();
}

以上步骤主要是取消事件挂载,解除主程序与UserControl对象之间的引用关系,及时让GC回收资源。

结果:

经最后测试,调用委托后,只会有当前最新的UserControl对象响应,界面切换时卡顿的现象消失。

扩展——.NET垃圾回收机制:

遇到这种情况,我们必须对.NET的垃圾回收机制有一定了解,借此机会聊聊GC回收机制。以下内容参考《CLR via C#》(第四版)(CLR: Common Language Runtime,公共语言运行时):

  • 在C#中,当我们创建一个对象时,它的引用会被存储在栈(Stack)中,而对象的实际数据会被存储在堆(Heap)中。这是.NET运行时自动进行的内存管理,称为垃圾回收(Garbage Collection, GC)。
  • 每个对象都有两个开销字段:类型对象指针和同步块索引,在64位应用程序中各占8个字节。
  • 所有引用类型的变量都称为根对象,这些根对象是垃圾回收的起点,括局部变量、全局变量、静态字段等‌;
  • CLR开始回收时会暂停所有线程,,防止线程在CLR检查期间访问对象并更改其状态;
  • 然后CLR进入GC的标记阶段,CLR遍历堆中的所有对象,将同步索引块中的一位设为0;
  • 然后CLR检查所有活动根,任何根引用了堆上的对象,CLR都会标记那个对象,将同步索引块中的位设为1;
  • 一个对象被标记后,CLR会检查那个对象的根,标记他们引用的对象,如果对象已经被标记,就不重新检查对象的字段,避免引起死循环;
  • 检查结束后,已标记的对象至少有一个根在引用,我们说这种对象是可达的,不能被垃圾回收;
  • GC删除未被标记的对象,把被标记的对象挪到一块连续的空间,进行压缩,避免空间碎片化的问题;
  • 作为压缩的一个步骤,CLR还要从每个根减去所引用对象在内存中的偏移字节数,保证每个根引用的是之前的对象;
  • 最后恢复执行之前被暂停的线程;

总结:

给对象事件绑定委托,释放对象前,要主动解除委托绑定,避免出现资源无法释放的问题出现;

CLR是基于代的垃圾回收机制,自动工作并不定时释放不会再被访问的资源。

由C#委托回调想到的二三事的更多相关文章

  1. 【转】第5篇:Xilium CefGlue 关于 CLR Object 与 JS 交互类库封装报告:自动注册JS脚本+委托回调方法分析

    作者: 牛A与牛C之间 时间: 2013-11-19 分类: 技术文章 | 暂无评论 | 编辑文章 主页 » 技术文章 » 第5篇:Xilium CefGlue 关于 CLR Object 与 JS ...

  2. 【转】第4篇:Xilium CefGlue 关于 CLR Object 与 JS 交互类库封装报告:委托回调方法分析

    作者: 牛A与牛C之间 时间: 2013-11-18 分类: 技术文章 | 暂无评论 | 编辑文章 主页 » 技术文章 » 第4篇:Xilium CefGlue 关于 CLR Object 与 JS ...

  3. Java并发编程二三事

    Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...

  4. C#编程语言之委托与事件(二)—— C#事件

    前面已经大致讲述了C#委托的一些基础知识点,本文接下来的内容是C#中的事件(Event),在此我提个建议,如果是刚接触C#的委托类型的朋友可以先看到这里,等熟悉了委托的使用之后(大约1-2天)再来了解 ...

  5. iOS7下滑动返回与ScrollView共存二三事

    [转载请注明出处] = =不是整篇复制就算注明出处了亲... iOS7下滑动返回与ScrollView共存二三事 [前情回顾] 去年的时候,写了这篇帖子iOS7滑动返回.文中提到,对于多页面结构的应用 ...

  6. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  7. 【C#夯实】我与接口二三事:IEnumerable、IQueryable 与 LINQ

    序 学生时期,有过小组作业,当时分工一人做那么两三个页面,然而在前端差不多的时候,我和另一个同学发生了争执.当时用的是简单的三层架构(DLL.BLL.UI),我个人觉得各写各的吧,到时候合并,而他觉得 ...

  8. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  9. linux杂记(十二?) 关于账号和密码的二三事

    关于密码的二三事 关于账号和密码的二三事 久了不更linux的相关知识,实在是懒得想内容点(纯粹是懒).那么今天就来谈谈关于linux密码和账号的重要概念. 假如你的主机遭到入侵,那么对方的第一个侵入 ...

  10. 对“demo!demo.Index+HookProc::Invoke”垃圾收集的类型已委托回调。这可能会导致应用程序崩溃、损坏和数据丢失。当传递委托给非托管代码,托管应用程序必须让这些委托保持活着

    对"demo!demo.Index+HookProc::Invoke"垃圾收集的类型已委托回调.这可能会导致应用程序崩溃.损坏和数据丢失.当传递委托给非托管代码,托管应用程序必须承 ...

随机推荐

  1. ubuntu18.04 heirloom-mailx 通过外部SMTP服务器发送邮件

    配置软件源ubuntu18.04上无法直接安装heirloom-mailx,需要添加软件源 sudo vi /etc/apt/sources.list写入 deb http://cz.archive. ...

  2. TwinCAT3 - 实现CiA402

    目录 1,起缘 2,想办法 3,开搞 3.1,CANOpen通信 3.1.1 对象字典 3.1.2 通信建立 3.2,CiA402伺服状态机 3.3,伺服运行 3.3.1 操作模式 3.3.2 轮廓位 ...

  3. LaTeX 常用引用标签前缀

    引用对象 标签前缀 Chapter ch Section sec Subsection sec Appendix app Figure fig Table tab List item itm Equa ...

  4. 【Mac渗透测试】之SQL注入Demo

    目录: 一.下载安装sqlmap 二.SQL注入 三.参考文章 一.下载安装sqlmap 1.官网地址:http://sqlmap.org/#download git下载: git clone --d ...

  5. compileSdkVersion, minSdkVersion 和 targetSdkVersion,傻傻分不清楚【转】

    原文 https://blog.csdn.net/gaolh89/article/details/79809034 在Android Studio项目的app/build.gradle中,我们可以看到 ...

  6. redis zset 使用场景

    前文,我们讨论过redis 的数据结构及使用场景.可参考: 参考: 总结篇4:redis 核心数据存储结构及核心业务模型实现应用场景 https://www.cnblogs.com/yizhiamum ...

  7. 戴尔笔记本游匣DELL G16 7620更换固态硬盘从选购固态硬盘到系统和应用程序迁移(克隆)全过程(教程)

    又到了捣鼓电脑的时候了.去年(2022年)8月14日买的电脑,当时7月份刚出戴尔游匣G16,搜了一下,2022年7月22日,戴尔首发游匣G16国行版本. 到现在也就用了差不多半年的时间,我的内存满了, ...

  8. SQL Server – Work with JSON

    前言 JSON 是一个很好的格式, array, object 就能表达一个表格了. 如果想保存一些结构格式, 又不想用表格这么大费周章的话, JSON 会是很好选择. 比如我用它来记入 Audit ...

  9. 【渗透测试】ATT&CK靶场一,phpmyadmin,域渗透,内网横向移动攻略

    前言 VulnStack,作为红日安全团队匠心打造的知识平台,其独特优势在于全面模拟了国内企业的实际业务场景,涵盖了CMS.漏洞管理及域管理等核心要素.这一设计理念源于红日安全团队对ATT&C ...

  10. JAVAEE——maven安装

    一.安装本地Maven 注意:检查JAVA_HOME环境变量, maven本身就是java写的,所以要求必须先安装JDK,检查本机jak环境win+r后输入cmd,然后输入java -version, ...