如何正确注销事件处理程序?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (41)

在代码审查中,我偶然发现了这个(简化的)代码片段来取消注册一个事件处理程序:

 Fire -= new MyDelegate(OnFire);

我认为这不会取消注册事件处理程序,因为它会创建一个以前从未注册过的新代理程序。但是搜索MSDN我发现了几个使用这个习惯用法的代码示例。

所以我开始了一个实验:

internal class Program
{
    public delegate void MyDelegate(string msg);
    public static event MyDelegate Fire;

    private static void Main(string[] args)
    {
        Fire += new MyDelegate(OnFire);
        Fire += new MyDelegate(OnFire);
        Fire("Hello 1");
        Fire -= new MyDelegate(OnFire);
        Fire("Hello 2");
        Fire -= new MyDelegate(OnFire);
        Fire("Hello 3");
    }

    private static void OnFire(string msg)
    {
        Console.WriteLine("OnFire: {0}", msg);
    }

}

令我惊讶的是,发生了以下情况:

  1. Fire("Hello 1"); 如预期的那样产生了两条消息。
  2. Fire("Hello 2");产生了一条消息!
  3. Fire("Hello 3");扔了一个NullReferenceException

我知道,对于事件处理程序和委托,编译器会在场景后面生成大量代码。但我仍然不明白为什么我的推理是错误的。

我错过了什么?

提问于
用户回答回答于

C#编译器的添加事件处理程序调用的默认实现Delegate.Combine,同时删除事件处理程序调用Delegate.Remove

Fire = (MyDelegate) Delegate.Remove(Fire, new MyDelegate(Program.OnFire));

框架的实现Delegate.Remove不会看MyDelegate对象本身,而是看代理引用的方法(Program.OnFire)。正因为如此,C#编译器允许你在添加/删除事件处理程序时使用简写语法:你可以省略该new MyDelegate部分:

Fire += OnFire;
Fire -= OnFire;

当最后一个委托从事件处理程序中被移除时,Delegate.Remove返回null。正如你发现的那样,在提高它之前检查事件对null是很重要的:

MyDelegate handler = Fire;
if (handler != null)
    handler("Hello 3");

它被分配到一个临时的局部变量,以防止在其他线程上取消订阅事件处理程序的可能竞争条件。防御此问题的另一种方法是创建一个始终订阅的空代理程序; 尽管这会使用更多的内存,但事件处理程序永远不能为null(并且代码可能更简单):

public static event MyDelegate Fire = delegate { };

热门问答

腾讯加固助手不能下载了吗?下载地址在哪里?MAC可以下载吗?我看只能在网页上加固,自己签名。?

腾讯云@移动安全

腾讯 · 移动开发工程师 (已认证)

腾讯云移动安全前端开发
推荐

乐固客户端已下线,可以使用移动应用在线加固https://console.cloud.tencent.com/ms/reinforce/list

可以自行签名打渠道包。

能提供腾讯区块链TBaas技术上链对接的流程图吗?

您好,非常感谢您的建议,我们目前确实没有整理完整的流程图,我们会尽快补齐并上线。 文字版的流程您可以先参考一下: 1,梳理业务场景和流程 2,规划区块链网络规模 3,购买区块链节点、组建区块链网络 4,根据业务场景编写智能合约 5,应用系统调用区块链的智能合约,与链交互 如果...... 展开详请

关于云直播的几个问题,望大佬解惑?

学生路人
推荐

1 海外单独计费

2 地址都是自己算的 可以变也可以不变

3 这个接口是拉流转推的 ,和播放不是一个东西哈 。

springboot项目启动报错找不到ConfigurationPropertiesBean?

TencentOS例程,keil5编译报错no such file or directory?

Supowang

腾讯 · 高级工程师 (已认证)

腾讯物联网操作系统TencentOS tiny产品接口人---岁寒知松柏,做最好的自己!
推荐
您好!您的代码是早期的代码,请使用最新的github代码,代码下载地址:https://github.com/Tencent/TencentOS-tiny 或者腾讯工蜂 https://git.code.tencent.com/Tencent_Open_Source/Tence...... 展开详请

我用spark多个特征处理算子对原始数据进行处理,最后通过决策树训练分类,请问怎么导出最终模型?

腾讯智能钛AI开发者

腾讯云 · 智能钛产品团队 (已认证)

腾讯智能钛产品团队官方运营账号。分享产品最新动态,第一时间解答用户疑问。
推荐

您好,如果是使用组件自己编写的代码,可以使用 spark 中的 pipeline 功能,将多个功能串成一个流水线,再把流水线导出为 pmml 模型。详见 https://github.com/jpmml/jpmml-sparkml。

所属标签

扫码关注云+社区

领取腾讯云代金券