C#事件访问器(Event)

事件介绍

和委托类似,事件是后期绑定机制。 实际上,事件是建立在对委托的语言支持之上的。

事件是对象用于(向系统中的所有相关组件)广播已发生事情的一种方式。 任何其他组件都可以订阅事件,并在

事件引发时得到通知。

通过订阅事件,还可在两个对象(事件源和事件接收器)之间创建耦合。 需要确保当不再对事件感兴趣时,事件接

收器将从事件源取消订阅。

事件的访问器作用

涉及到线程安全的事件添加和移除操作。其作用是确保在多线程环境下对事件处理程序的添加和移除是安全的。具体来说:

  1. 线程安全 :使用 Interlocked.CompareExchange 确保在多个线程同时操作事件处理程序时,不会导致数据竞争或更新不一致的问题。

  2. 添加处理程序add 访问器将新的处理程序添加到事件中,通过 Delegate.Combine 合并现有的处理程序和新的处理程序。

  3. 移除处理程序remove 访问器从事件中移除指定的处理程序,通过 Delegate.Remove 从现有处理程序列表中移除目标处理程序。

演示如何实现自定义的 add 和 remove 事件访问器。 虽然可以替换访问器内的任何代码,但建议先锁定事件,再添加或删除新的事件处理程序方法。

cs 复制代码
event EventHandler IDrawingObject.OnDraw
{
    add
    {
        lock (objectLock)
        {
            PreDrawEvent += value;
        }
    }
    remove
    {
        lock (objectLock)
        {
            PreDrawEvent -= value;
        }
    }
}

下面这个示范的事件访问器是我再写tcp通讯的时候用到的,因为代码比较多就列举了一个接收事件

cs 复制代码
 private MyModbusSlave.ReceviceByteEventHandler _ReceviceByteEventHandler;

 public delegate void ReceviceByteEventHandler(byte[] date);


//确保在多个线程同时进行事件处理程序的添加和移除时,操作是线程安全的。这种实现方式可以防止数据竞争和并发问题。
[Category("ModbusTcp事件")]
[Description("接收Byte数据事件")]
public event ReceviceByteEventHandler OnReceviceByte
{
    add
    {
        //取得当前事件处理程序列表的副本。_ReceviceByteEventHandler 是存储事件处理程序的字段。
        ReceviceByteEventHandler receviceByteEventHandler = this._ReceviceByteEventHandler;
        //临时变量,用于在更新事件处理程序时进行比较。
        ReceviceByteEventHandler temp;
        //使用 do-while 循环来确保事件处理程序的更新是原子性的。如果在更新过程中,事件处理程序的列表被其他线程修改了,Interlocked.CompareExchange 将返回修改后的值,循环会重新尝试更新。
        do
        {
            temp = receviceByteEventHandler;
            //使用 Delegate.Combine 将新的处理程序 value 添加到当前事件处理程序列表中,并返回更新后的事件处理程序列表。
            ReceviceByteEventHandler value2 = (ReceviceByteEventHandler)Delegate.Combine(receviceByteEventHandler);
            //尝试将 _ReceviceByteEventHandler 更新为 value2。如果 _ReceviceByteEventHandler 的值等于 temp(表示在此期间没有其他线程修改事件处理程序列表),则更新成功。
            receviceByteEventHandler = Interlocked.CompareExchange(ref _ReceviceByteEventHandler, value2, temp);
        } while (receviceByteEventHandler != temp);
    }
    //取得当前事件处理程序列表的副本。
    remove
    {
        ReceviceByteEventHandler receviceByteEventHandler = this._ReceviceByteEventHandler;
        ReceviceByteEventHandler temp;
        do
        {
            temp = receviceByteEventHandler;
            //使用 Delegate.Remove 从当前事件处理程序列表中移除指定的处理程序 value,并返回更新后的事件处理程序列表。
            ReceviceByteEventHandler value2 = (ReceviceByteEventHandler)Delegate.Remove(temp, value);
            receviceByteEventHandler = Interlocked.CompareExchange(ref _ReceviceByteEventHandler, value2, temp);
        } while (receviceByteEventHandler != temp);
    }
}

通过 Interlocked.CompareExchangeDelegate.Combine / Delegate.Remove 实现了线程安全的事件处理程序添加和移除操作。这样可以避免在多线程环境下因并发修改事件处理程序列表而导致的问题,确保事件的处理程序在多个线程中正确地添加和移除。

什么情况下不使用事件访问器

在不使用这种线程安全的事件访问器的情况下,你可能会面临多线程环境下的竞态条件和数据一致性问题。

如果你的应用程序只在单线程环境下运行,或者你能够确保事件的添加和移除操作不会发生在多个线程之间的竞争,那么你可以不使用这种线程安全的实现。

但在大多数情况下,尤其是在复杂的多线程应用中,保持线程安全是很重要的,建议继续使用这种方式来避免潜在的问题。

想要更详细的了解和学习事件可以去这里:

如何实现自定义事件访问器 - C# | Microsoft Learn

相关推荐
geovindu28 分钟前
CSharp: Command Pattern
开发语言·后端·c#·.net·.netcore·命令模式·行为模式
心易行者1 小时前
Python自动化测试7步落地法:用python在线运行省掉90%环境配置时间
java·开发语言·人工智能·python·log4j·ai编程
tedcloud1231 小时前
OpenLogi 怎么搭建?用 Rust 打造一个轻量的 Logitech 外设管理工具
linux·运维·服务器·开发语言·后端·rust·开源
Tanjia_kiki2 小时前
谷歌浏览器中F12编辑并重发请求
开发语言·前端·javascript
程序猿编码2 小时前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
传奇开心果编程2 小时前
【Rust入门知识点学与练】第5课:函数
开发语言·学习·rust
秋田君2 小时前
Qt_QMediaPlayer类与QMediaPlaylist类
开发语言·qt
统计学小王子2 小时前
数学建模国赛倒计时6天——《软件工具(R语言精讲)》
开发语言·数学建模·r语言
C++ 老炮儿的技术栈3 小时前
Qt5 使用 QPainter 绘制阿基米德螺线
开发语言·c++·windows·qt·代码化
EasyGBS3 小时前
从接入到稳定播放:无插件直播H5视频流媒体播放器EasyPlayer.js如何撑起Web端流媒体
开发语言·前端·javascript