首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >正确调用事件处理程序

正确调用事件处理程序

作者头像
喵叔
发布2020-09-08 17:07:34
发布2020-09-08 17:07:34
1.3K00
代码可运行
举报
文章被收录于专栏:喵叔's 专栏喵叔's 专栏
运行总次数:0
代码可运行

不管是刚接触 C# 还是已经具有多年开发经验的大部分人会觉得事件处理很简单,只需要把事件定义好然后在需要的时候出发它就可以了。其实这种想法是错误的,这里面有很多需要注意的问题。下面这段代码是大部分开发人员经常使用的定义事件处理程序的方法。

代码语言:javascript
代码运行次数:0
运行
复制
public class EventDemo
{
    private EventHandler<int> demo;
    public void DemoEvent()
    {
        demo(this);
    }
}

上面的代码中存在一个严重的问题,当在对象上触发 demo 事件时并没有关联的事件处理程序的话,C# 将会用 null 值来表示没有处理程序与该事件相关联,进而将会引发 NullReferenceException 异常。针对这个问题大部分程序员会做如下修改:

代码语言:javascript
代码运行次数:0
运行
复制
public void DemoEvent()
{
    if(demo!=null)
    {
        demo(this);
    }
}

这种修改方法解决了上述大部分问题,但是还存在一个隐藏的问题。当有多个线程都调用这个事件是就会出现线程之间相互争夺,举个例子来说就是线程 A 在执行到 if (demo!=null)时发现 demo 不等于 null ,正巧这时线程 B 将唯一的事件处理程序解除了订阅,这时线程 A 再调用 demo 时事件处理程序已经变为了 null ,进而导致 NullReferenceException 异常。真多这个问题一些程序员又会做如下的修改:

代码语言:javascript
代码运行次数:0
运行
复制
public void DemoEvent()
{
    var handler = demo;
    if(handler!=null)
    {
        handler(this);
    }
}

上述这种方法是对等号右侧的内容进行了浅拷贝创建了新的引用,使其指向原来的事件处理程序(相当于给事件订阅者生成了一个快照),当另一个进程注销掉事件处理程序时,注销的只是 demo 上所绑定的处理程序,因此当当前的线程执行 handler 时是不会出现 NullReferenceException 异常。这种解决方法是网上所能搜的方法之一,也是绝大部分开发人员所推荐的解决方法。但是这个方法会使代码显得难以理解(尤其是对于开发新手),并且代码稍显冗余。于是在 C# 6.0 中微软为我们增加了 null 条件运算符(?.)。null 条件运算符可以安全的调用事件处理程序并且使代码清晰明了还简单。首先它会判断运算符左侧的内容是否为 null ,如果是 null 就跳过该语句,反之执行运算符右侧的内容。下面我们利用 null 条件运算符对前面的代码进行一下改进。

代码语言:javascript
代码运行次数:0
运行
复制
public void DemoEvent()
{
    demo?.Invoke(this);
}

Tip:使用 null 条件运算符有一点需要注意,运算符右侧不允许直接出现括号,因此必须使用 Invoke 进行触发事件。每定义一个委托或者时间编译器就会生成一个 Invoke 方法。

进行触发事件。每定义一个委托或者时间编译器就会生成一个 Invoke 方法。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/03/29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档