首页 > 编程语言 >由C#委托回调想到的二三事

由C#委托回调想到的二三事

时间:2024-11-15 17:18:49浏览次数:1  
标签:控件 委托 C# 回收 二三 对象 UserControl CLR

写在前面:
之前的过开发程中,我愈发觉得面对复杂的界面要求,最好还是用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#,回收,二三,对象,UserControl,CLR
From: https://www.cnblogs.com/geekfrank/p/18548348

相关文章

  • GoFurther - Chapter 5: Database Setup and Configuration
     zzh@ZZHPC:/zdata/Github/greenlight$dockerpullpostgreszzh@ZZHPC:/zdata/Github/greenlight$dockerrun--namepostgres17-p5432:5432-ePOSTGRES_USER=root-ePOSTGRES_PASSWORD=root-dpostgres:latest zzh@ZZHPC:~$dockerexec-itpostgres17psql......
  • 哋它亢技术(DataCon) WiKi专题——0.目录大纲
    哋它亢技术-WiKi专题哋它亢WiKi专栏文章列表目录大纲1.哋它亢的历史与起源2.哋它亢的技术原理3.哋它亢的应用领域4.哋它亢的技术挑战5.哋它亢的市场与产业影响6.哋它亢的未来展望结语哋它亢WiKi随着新技术层出不穷,一些充满潜力的创新开始崭露头角,并迅速引起了全......
  • 代理模式在JavaScript中的恋爱应用笔记
    一、引言在面向对象编程的世界里,代理模式犹如一位巧妙的媒人,巧妙地连接了两个对象之间的交互,而无需直接显式地引用彼此。这种模式不仅降低了系统的耦合度,还使得代码更加灵活、可扩展。而在JavaScript的世界里,代理模式更是展现出了其独特的魅力。今天,我将结合恋爱场景,为大家......
  • 神奇的getchar()
    C语言中的getchar()文章目录前言一、getchar是什么?二、使用步骤总结前言getchar()是C语言标准库中的一个函数,它用于从标准输入流(通常是键盘)读取下一个可用的字符。提示:以下是本篇文章正文内容,下面案例仅供参考一、getchar()是什么?getchar()是一个非常基础的......
  • 解码JavaScript作用域:var a = 1; 背后的故事
    1、引言JavaScript是一种广泛使用的编程语言,其灵活的语法和强大的功能使其成为前端开发的首选语言。然而,对于初学者来说,理解JavaScript中的作用域机制可能会有些困难。本文将通过一句简单的代码vara=1;,深入解析JavaScript的执行机制和作用域管理。2、变量声明与......
  • SELF-REFINE: Iterative Refinement with Self-Feedback
    1.概述基于给定的Prompt,大语言模型生成的Reponse可能不是最好的(这一点我认为当前的LLM大部分都是Decoder架构,基于已生成的结果产生下一个Token,一旦之前生成结果出错,也不容易及时改正。)。本文为原始的生成添加了额外的反思重写步骤,过程如下:对于给定的\(Input\),先交给LLM生成......
  • STM32一种计算CPU使用率的方法及其实现原理
    本文将以STM32F429+FreeRTOS+KEIL为测试环境,看下MCU的使用率1、计算STM32使用率的官方方法在其CubeMX的固件库中2、加入自己的工程2.1、文件cpu_utils.c有描述使用的步骤2.2、实操一遍第一步:将上图中的cpu_utils.c文件添加到工程中,并将其头文件路径加......
  • Docker不再神秘 ------Ubuntu20.04 安装Docker 及实用技巧,建议收藏
    Dockerdocker是一种容器,简而言之就是别人把一堆环境配置好了,你可以下载下来直接拿来使用(我的个人理解),有点像虚拟机你知道吧。比如下面这样,我直接打开了一个小电脑(docker),里面桌面啊、root啊全都有,跟你ubuntu系统类似,单说细节还不完全一样,毕竟它轻便哈哈……也有一些对比这下......
  • 第6篇 Scrum 冲刺博客
    作业要求这个作业属于哪个课程计科34班这个作业的要求在哪里团队作业4——项目冲刺这个作业的目标1.站立式会议2.发布项目燃尽图3.每人的代码/文档签入记录4.适当的项目程序/模块的最新(运行)截图5.每日每人总结会议照片昨日已完成的工作/今天计划完成的工作......
  • 前端CSS规范
     分号每个属性声明后面都要加分号。命名不使用id选择器适用有意义的名词命名单词全部小写,名词超过1个时,使用-分隔符属性声明顺序原则:整体到局部,外部到内部,重要属性优先.element{display:block;float:left;position:absolute;top:0;......