谈谈C#中的事件注册和注销

Unity插件EasyButton中委托事件的注册和调用 //消息传递Message //时间委托delegate&&event Easy Button事件监听的两种方式 (1).Message ①SeedMessage 向游戏物体发消息,遍历所有脚本中查找符合的定义的方法 然后调用 ②SeedMessageUpwards 向上父类遍历 ③BroadcastMessage 向下子类遍历 (2).Event 调用注册上的方法,是固定 阅读详情
由于 .NET 框架对消息循环机制进行了很好的封装,开发人员不再需要深入的了解 Windows 事件 / 消息实现的具体机制,也无需创建复杂的事件结构体和所谓的消息句柄。我们所要做的无非就是—— 1 、使用重载运算符“ += ”注册一个事件; 2 、编写对于该事件的处理方法。(关于C#2.0中事件处理的相关介绍,请参阅我的文章:C#2.0的泛型代理和事件 :以一当百的快感 )
 
如此简单,以至于习惯了 Win32 编程的伙计们对此嗤之以鼻,讽之:“我们是开手排挡车的专业选手,你们 .NET 一族只能玩玩自动档。什么? 你们还看《头文字 D 》?能看懂吗?” …
 
不理他们!咱们说咱们的。转头前甩给他们一句话:“迂腐 ! ”。如果不解恨,那么在引用一段名师的话:“我们从不乐意改变自己的工作习惯,就像把妻子的照片放在台灯下面一样。然而,当一种新的方法确实能极大的提高我们的工作效率和行动力时,我们干嘛要固执呢?”——够效果了吧?
 
言归正传。
 
前几天,我在编写主窗体与子模块的事件通信时,遇到了一个极其堪称郁闷的问题。说这个问题前,我和大家交代一下我的设计思路。

主窗体 (frmMain : IParentForm)
事件成员:
public event ParentEventHandler OnUserListCreated;
 
事件处理方法:
void ToDoOnRequestUserList(object sender, EventArgs e){
         // 创建 DataTable dt
         …
         This.OnUserListCreated(this, new ParentEventArgs(dt));
}
 
某一行注册子窗体事件:
frmChild.OnRequestUserList += new EventHandler (ToDoOnRequestUserList);

子窗体 (frmChild)
事件成员:
public event EventHandler OnUserListCreated;
 
事件处理方法:
void ToDoOnRequestUserReturned(object sender, ParentEventArgs e){}
 
在 OnLoad 事件处理方法中注册主窗体的事件:
(this.MdiParent as IParentForm). OnUserListCreated += new ParentEventHandler (ToDoOnRequestUserReturned);

 
主窗体对象为 frmMain ,它实现了 IParentForm 接口,该接口定义了事件成员 OnUserListCreated( 它的 EventArgs 为自定义的 ParentEventArgs) 。 frmMain 对象在某处创建 了一个子窗体 frmChild ,并注册了 frmChild 的事件 OnRequestUserList 。
 
子窗体对象 frmChild 在载入时 (OnLoad 方法中 ) 获得 frmMain 的引用,并注册了 frmMain 的事件 OnUserListCreated 。
 
根据业务逻辑,子窗体运行的某一时刻,用户行为触发了事件 OnRequestUserList ,此时 frmMain 将捕获此事件并调用自身的处理方法生成一个被请求的用户列表 (DataTable) 。然后, frmMain 发出了事件 OnUserListCreated 以提示列表生成完毕,并将刚刚创建的 DataTable 作为 ParentEventArgs 参数插入事件中。随后,子窗体将接收到这个事件,并在自己的事件处理方法中对传来的 DataTable 进行自己的业务逻辑动作。
 
在接下来程序的运行中,可爱的代码心情愉悦地顺利执行 … 但是,好景不长!
 
当我将打开的子窗体关闭后再重新打开,主窗体在触发 OnUserListCreated 事件后发生调用目标异常 , 子窗体在该事件的处理方法中也抛出 NullReferenceException 异常 ( 未将对象引用设置到对象实例 ) 。当我在子窗体的事件处理方法 ToDoOnRequestUserReturned 中设置断点调试后发现:所有的控件、变量都为 null !!
 
那叫郁闷,那叫惆怅 … 公车上、步行中、如厕时、入睡前,我估摸着这种灵异现象可能与最近隔壁邻居家小猫的突然消失有着千丝万缕的联系 … 当然,作为基督教徒的我,也后怕这是主,耶稣基督对于我大前天横闯马路的惩罚 …
 
无助中,我极其盲目的在 frmChild 的 ToDoOnRequestUserReturned 方法中加入了一行语句:“ MessageBox.ShowDialog(“So boring a thing!”) ”以发泄心情。 保存、编译、运行——大坏蛋的面目露了出来!当我第一次打开子窗体的时候,如我所料,程序正常运行并弹出了 MessageBox 。关键是,当我关闭子窗口并第二次打开它执行时, MessageBox 弹出了两次! 恩 …
 
带着疑问,我重复了以上关闭、打开步骤, MessageBox 弹出了三次!——事情已经有了眉目。在我辗转反复的思考后(也许有人会骂我菜鸟 … ),终于明白了所有事情的缘由:
 
因为程序一直处在运行中,所以主窗体对象一直驻留内存中并保持着自身的状态(它没有的 disposed ),所以,每次子窗体创建时,主窗体都会注册它的 OnRequestUserList 事件,同样的,该子窗体在加载时,自身也会把主窗体的 OnUserListCreated 事件注册一次。
 
问题就出在这里,虽然子窗体关闭了,并 disposed 了。但是,它关闭时并没有把在主窗体注册的事件同时注销。随着子窗体一次次的打开,主窗体的 OnUserListCreated 就被 += 了 N 多了注册用户,其中的 N-1 个用户其实早已经不存在了,而主窗体全然不知。所以当发出 OnUserListCreated 事件后,主窗体还会以无反顾地去调用这 N 多个方法代理,这必然会导致异常抛出——唯一打开的那个子窗体接受到一次次传来的事件,并企图调用 ToDoOnUserListReturned 方法,如果此方法中包含着对本对象成员变量的操作,自然会引出“未将引用设置到对象实例”的异常。
 
也许有朋友会问,为什么主窗体调用那些早已 disposed 的 frmChild 的方法的代理时,会被当前存在的那个 frmChild 执行呢? 我认为这可能是由于类实例的同一个方法在内存栈中共享空间造成的;而成员变量在堆中存放,各自维护其状态,当其所属的对象被释放回收时,其值也就置为 null 了。(个人观点,望兄弟姐们给予指正)
 
综上,我做一下总结:
 
子窗体在关闭时,应当把自己注册的主窗体对象(或者是长久驻留内存对象)事件一一注销。例如本例中,应在子窗体的 OnClosed 事件处理方法中加入以下代码:
(this.MdiParent as IParentForm). OnUserListCreated  -=  new ParentEventHandler    (ToDoOnRequestUserReturned)
 
如果仅仅是为了在主窗体执行完某项操作后触发子窗体某一方法的执行,我们通常不采用事件机制,而采用以下两种方法:

A.
将此方法访问属性改为 public ,然后由主窗体适时调用。
B.
定义一个接口,子窗体对象实现这个接口,并把该目标方法提升为该接口的成员。由主窗体适时调用这个接口成员方法。
掌握C#微服务的服务注册与注销——深入解析Consul的使用之道 通过本文的学习,你应该已经掌握了如何在C#微服务项目中使用Consul进行服务注册与注销的基本方法,并对一些高级特性有了初步的认识。无论是想要提升现有项目的稳定性,还是计划构建新的微服务架构,这些知识都将为你提供坚实的理论基础和技术支持。希望这篇文章能够激发你的兴趣,并为你的日常开发工作带来实质性的改进。 阅读详情

相关推荐

多次注册事件会导致一个事件被触发多次的解决方法

一个JavaScript逻辑,会自动绑定函数到按钮的click事件,但是这段代码会反复注册事件,具体的解决方法如下,感兴趣的朋友可以参考下

钓上我,就没那么容易甩掉!—— 谈谈C#中的事件注册和注销

由于.NET框架对消息循环机制进行了很好的封装,开发人员不再需要深入的了解Windows事件/消息实现的具体机制,也无需创建复杂的事件结构体和所谓的消息句柄。我们所要做的无非就是——1、使用重载运算符“+=”注册一个事件;2、编写对于该事件的处理方法。(关于C#2.0中事件处理的相关介绍,请参阅我的文章:C#2.0的泛型代理和事件 :以一当百的快感 document.title="C#2.0的

铀·绎 5270

Windows 10/11下Drive Composer Pro 2.9.0完整安装与连接ACS880变频器实战教程

本文详细介绍了在Windows 10/11系统上安装Drive Composer Pro 2.9.0并连接ACS880变频器的完整教程。从系统环境准备、软件安装到许可证配置,再到USB、蓝牙和以太网三种连接方式的实战操作,帮助工业自动化工程师快速掌握ABB变频器的调试技巧。

weixin_32956805的博客 528

C# 在类中如何注册事件与取消事件注册?

以:按钮的单击来举例 名为 button1首先:注册按钮单击事件的方法为:button1.Click += new System.EventHandler(this.button1_Click);取消注册事件的方法为:button1.Click -= new System.EventHandler(this.button1_Click);但我想实现如下的效果怎么做? 把 but

yazhoucom的专栏 7001

三维变换矩阵实战——三维点云的旋转、缩放、镜像、错切、平移、正交投影

旋转矩阵:右边矩阵是点云的原始坐标,左边的是旋转矩阵可视化:绕x轴旋转90度。

weixin_43850909的博客 8230

振动信号相位测量的关键技术解析

本文深入解析了振动信号相位测量的关键技术,强调相位是振动信号的“身份证”,对于动平衡和故障诊断至关重要。文章详细阐述了键相传感器与振动传感器的选型、安装要点,以及时域和频域(FFT)核心算法,并针对现场噪声、转速波动等挑战提供了实战解决方案,帮助工程师实现精准、稳定的相位测量。

vv45678的博客 838

c#的event没有一次性的,而且使用相同的函数登记登记两次,也同样会调用两次...

RT 转载于:https://www.cnblogs.com/zhengwenwei/archive/2011/10/12/2208408.html

weixin_30547797的博客 384

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。还原数据库时把序列取消勾选,选择不还原序列。因为还原表的同时,会自动将自增序列还原上去,此处勾选会导致数据库重复创建序列。点击还原数据库完毕后,可以看到序列仅有一个,没有出现重复序列。但此时的序列当前值是从1开始的,需要重置。点击菜单“其它”-》“序列”,可以看到有多个重复序列。

奋斗鱼 1043

c# winform 通过编程取消事件(event)的注册

我们知道如果是委托的话我们可以让委托(delegate)对象等于null 这样的话可以取消委托时间的注册,但是如何取消事件(event)的注册呢?     废话少说了,调用下边的方法: public static void RemoveEvent(System.Windows

SunLeibo的专栏 5440

二阶高通滤波器硬件设计:5种RC到有源方案对比与选型

在传感器前端和音频信号调理中,二阶高通滤波器是抑制直流偏移与低频干扰的关键模块。其设计核心围绕截止频率、品质因数Q、增益与噪声四个指标展开,其中Q值决定滤波器在转折频率附近的响应形状,如Butterworth最大平坦响应需Q=0.707。从纯无源RC电路到引入运放的有源拓扑,演变逻辑在于解决负载效应、提供增益并借助反馈提升Q值。实际工程中,RC级联、VCVS(Sallen-Key)、状态变量及回转器模拟电感等方案各有适用场景:RC电路简单但Q值受限,Sallen-Key单运放即可实现高Q,状态变量适合Q值独

weixin_32770423的博客 103

衡山派Luban-Lite系统SDIO无线网络配置实战:以D133CBV-QFN88开发板与RTL8189模组为例

本文详细介绍了在衡山派D133CBV-QFN88开发板上,为Luban-Lite系统配置SDIO无线网络的完整实战流程。通过配置SDMC接口、RTL8189 Wi-Fi驱动、lwIP协议栈及内核参数,并解决编译分区问题,最终实现Wi-Fi网络连接与测试,为嵌入式设备联网提供清晰指导。

weixin_35677363的博客 270

谈谈C#中的事件中多次调用的注册和注销

由于.NET框架对消息循环机制进行了很好的封装,开发人员不再需要深入的了解Windows事件/消息实现的具体机制,也无需创建复杂的事件结构体和所谓的消息句柄。我们所要做的无非就是——1、使用重载运算符“+=”注册一个事件;2、编写对于该事件的处理方法。(关于C#2.0中事件处理的相关介绍,请参阅我的文章:C#2.0的泛型代理和事件 :以一当百的快感) 如此简单,以至于习惯了Win3

Steven Lei的博客 6240

3GPP Release与UE Category:无线通信标准演进与终端能力分级全解析

在蜂窝移动通信领域,技术标准的演进与终端设备的能力分级是理解整个产业发展的核心框架。其基本原理由国际标准组织3GPP通过周期性发布的技术规范版本(Release)来定义,每个Release都像一份阶段性的技术蓝图,规定了网络与终端应支持的新功能与性能基线。这种标准化机制的技术价值在于确保了全球设备的互联互通与网络的平滑升级,为从4G LTE到5G NR乃至未来的演进提供了清晰路径。在这一框架下,终端能力分级(如LTE时代的UE Category)是将标准转化为具体设备性能的关键环节,它直接定义了终端在射频和

weixin_34098209的博客 331

C#中的事件注册和注销

由于.NET框架对消息循环机制进行了很好的封装,开发人员不再需要深入的了解Windows事件/消息实现的具体机制,也无需创建复杂的事件结构体和所谓的消息句柄。我们所要做的无非就是——1、使用重载运算符“+=”注册一个事件;2、编写对于该事件的处理方法。(关于C#2.0中事件处理的相关介绍,请参阅我的文章:C#2.0的泛型代理和事件 :以一当百的快感) 如此简单,以至于习惯了Win32...

dav9029的博客 1686

linux操作系统使用以太网连接ubuntu,设置固定ipv4

首先,您需要确定要配置的网络接口。可以使用ifconfig -a或ip addr命令来列出当前系统上的所有网络接口及其相关信息。找到您要配置的网络接口名称,例如eth0、ens33或enp0s3;保存并关闭文件后,您需要重启网络服务以使更改生效。在linux操作系统中,要使用交换机连接以太网可以直接使用交互界面;如果你的操作系统没有交互界面,你可以使用命令方式配置;点击ipv4,方式为手动,可以设置ip和子网了;开头,后接网络接口名称。第三步:点击有线最右边圆形控件;第二步:点击有线设置;

2301_79311842的博客 2404

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C#窗体程序连接SQL Server数据库实现账号登录、账号注册、修改密码、账号注销和实名认证(不定时更新)

一个简易的C#窗体登录程序,可以实现账号登录、注册、修改和注销。

qq_60423778的博客 1万+

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C#中事件

一、事件基础 1、事件简介 事件常用于对某一消息作出的反应。当某一事件被触发之后,就会对其做出对应的响应。 连接消息和反馈动作的符号为+=,符号左侧表示发出的消息,右侧表示对消息做出的响应函数 2、事件提示及查看 在输入对象加点号后,VS会出现智能提示,其分别代表: 小扳手:表示属性 立方体:表示函数或方法 闪电:表示事件 二、常见事件示例 1、定时器事件Timer 要包含对应的命名空间:using System.Timers; 指定时间间隔,然后每到指定的间隔就响应一次,代码如下:

Jeffxu_lib的博客 8151

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

用C++实现C#中的委托和事件

 C#中的测试代码:using System;namespace testEventApp{ public delegate void ClassSender_Event_Handler(object sender, string strEventArg); /// /// ClassReceiver 的摘要说明。 /// class ClassR

双脚踏实地 2028

事件订阅后内存居高不下?,深度剖析C#中未注销事件的性能灾难

解决C#事件内存泄漏难题,深入解析事件订阅与取消订阅机制。涵盖常见场景、弱事件模式及最佳实践,有效避免对象无法释放的性能问题。掌握正确订阅管理方法,提升应用稳定性,值得收藏。

FastProceed的博客 814

C#中的委托是什么?事件是不是一种委托?事件和委托的关系。

委托可以把一个方法作为参数代入另一个方法。 委托可以理解为指向一个函数的指针。 委托和事件没有可比性,因为委托是类型,事件是对象,下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别。事件的内部是用委托实现的。因为对于事件来讲,外部只能“注册自己+=、注销自己-=”,外界不可以注销其他的注册者,外界不可以主动触发事件,因此如果用Delegate就没法进行上面的...

weixin_33968104的博客 2803

C# 事件(event)

事件是一种特殊的多播委托,仅可以从声明事件的类(或派生类)或结构(发布服务器类)中对其进行调用。如果其他类或结构订阅该事件,则在发布服务器类引发该事件时,将调用其事件处理程序方法可以将事件标记为publicprivateprotectedinternal或。这些访问修饰符定义该类的用户访问该事件的方式。事件在我们平时开发中用的还是比较多的,大部分时候用在框架中,比如 Winform 中的 Timer 组件,如下面代码,Timer.Elapsed 就是一个事件//实例化Timer类,

熊思雨的博客 7775

【C#】事件与观察者模式

在面向对象的程序设计中,模块化是基本,而模块之间往往需要“通信”,事件机制便是最常用的方式之一。事件基于委托的基础上规定了自己的定义与用法。事件用于一对象改变时,与其相关的n个对象得到通知并发生改变,适用于对象之间因果关系明显的情况。

Eazey丶亦泽 1340

C#注销或系统关机等事件

一般情况下,在研发应用程式的时候都很少考虑一些系统部分的变化对应用程式带来影响,比如系统字体库里的字体增加或减少,用户注销或系统关机,桌面主题变换等等。针对应用程式的不同,或多或少会造成一些影响,比如程式在运行过程中没有退出就被用户注销,有可能造成数据丢失,系统字体的更改或系统时钟的更改对程式造成界面显示上或处理上的影响。假如需要程式有一定的健壮性,就需要在这些事件发生时做一些处理。幸

lemonxuwei的专栏 4532

C#委托与事件

C#委托与事件一、委托(C#中的委托就是C++中的回调函数)记得在使用C语言的年代,整个项目中都充满着针指的身影,那时候流行使用函数指针来创建回调函数,使用回调可以把函数回调给程序中的另一个函数。但函数指针只是简单地把地址指向另一个函数,并不能传递其他额外信息。在.NET中,在大部分时间里都没有指针的身影,因为指针被封闭在内部函数当中。可是回调函数却依然存在,它是以委托的方式来完成的。委托可以被视...

yuanxue_vaq的博客 994

委托的注册和注销

让我们来回顾一下委托的内容。

m0_74236534的博客 402
上一篇: Winform中在Form上截取消息的两种方法
下一篇: C#+Windows API操纵系统菜单
mzphp
博客等级 码龄21年 9粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值