发布时间:2026/7/5 5:00:51
引发事件的问题
引发事件是一个非常容易的事情, 但是的确也有它的误区. 让我们举个例子. 假设我们写个消息接收器, 每当我们收到一个新消息, 我们引发一个包含了新消息的事件MessageReceived.安装我们通常的方法,就是:public class MessageReceivedEventArgs : EventArgs { // 接收到的消息 public string Message { get; private set; } // 架构 ReceivedEventArgs public MessageReceivedEventArgs(string message) { Message message; } }接下来, 我们创建一个非线程安全访问的类UnsafeMessenger来实现这个消息同时通知所有的订阅者(subscriber).public class UnsafeMessenger { public event EventHandlerMessageReceivedEventArgs MessageReceived; // 当收到新消息时调用 public void OnNewMessage(string message) { if (MessageReceived ! null) { MessageReceived(this, new MessageReceivedEventArgs(message)); } } }注意, 通常OnNewMessage()是私有的, 但是在这里为了测试的方便,我们将它设为public.大功告成!! 是吗? 事实上, 如果我们是单线程的程序, 这的确已经足够, 但是这是非线程安全访问(thread-safe).为什么? 想想, 订阅者可以任何时候订阅或者取消订阅. 比如,我们当前有一个订阅者, 那么当接收到一个新消息,执行到这一句时:if (MessageReceived ! null)肯定会通过, 因为有一个订阅者, 如果这个时候, 这名订阅者执行了取消订阅的命令:myMessenger.MessageReceived - MyMessageHandler;那么MessageReceived委托 就为null 了,//已经通过了这个IF语句 if (MessageReceived ! null) { //MessageReceived委托 就为null 了, 但是我们将要执行这句 MessageReceived(this, new MessageReceivedEventArgs(message)); }这个时候, 就会引发NullReferenceException.方案一: 锁住它, 锁机制当允许多线程的时候, 我们可以用锁机制来避免一个用户在我们执行事件时订阅或者取消订阅, 或者在用户执行操作时, 不能引发事件.public class SyncronizedMessenger : IMessenger { // 委托和锁 private EventHandlerMessageReceivedEventArgs _messageReceived; private readonly object _raiseLock new object(); // 订阅/取消订阅的锁机制 public event EventHandlerMessageReceivedEventArgs MessageReceived { add { lock (_raiseLock) { _messageReceived value; } } remove { lock (_raiseLock) { _messageReceived - value; } } } // 引发事件的锁机制 public void OnNewMessage(string message) { lock (_raiseLock) { if (_messageReceived ! null) { _messageReceived(this, new MessageReceivedEventArgs(message)); } } } }这样, 如果有人试图订阅或取消订阅时, 必须要等待OnNewMessage事件的完成, 反之亦然.方案二: 永不为空, 默认加载一个订阅者我们面临的主要问题是有可能委托为空. 那么如果事先加载一个委托,会怎么样?public class EmptySubscriberMessenger : IMessenger { // 立刻给它一个空的订阅者 public event EventHandlerMessageReceivedEventArgs MessageReceived (s, e) { }; // 现在根本无需检查是否为 null! public void OnNewMessage(string message) { MessageReceived(this, new MessageReceivedEventArgs(message)); } }方案三: 创建一个本地的委托副本另外一个简单的方案, 也就是很多人都在使用的, 微软建议的模式: 创建一个本地的委托副本.public class LocalCopyMessenger : IMessenger { public event EventHandlerMessageReceivedEventArgs MessageReceived; // 当我们引发事件时, 做一个副本 public void OnNewMessage(string message) { var target MessageReceived; if (target ! null) { target(this, new MessageReceivedEventArgs(message)); } } }下面是以上四种方法的效率, 在执行10亿次的重复操作时:以上参考翻译自: C#/.NET Fundamentals: Safely and Efficiently Raising Events小结有一种编程方式叫 Cargo Cult Programming, 中文名: 货物崇拜编程. 维基定义为其特征为不明就里地仪式性地使用代码或程序架构。货物崇拜编程通常是一个程序员既没理解他要解决的 bug也没理解表面上的解决方案的典型表现。这个名词有时也指不熟练的或没经验的程序员从某处拷贝代码到另一处却不太清楚其代码是如何工作的或者不清楚在新的地方是否需要这段代码。也可以指不正确或过份的应用设计模式代码风格或编程方法却对其原理不明就里。我承认在高举实用主义(敝人的如何做一个快乐的ASP.NET程序员) 的年代, 为了效率, 我也经常这样做.--试问谁有时间给第三方控件做测试?自从这个创建本地委托副本的方案被大牛们推荐后, 大家都在用, 有人也不一定明白它背后的故事.有时间的朋友们聊聊.net中的野史, 谈笑间扩充一点编程的能力,总比聊哪个明星又被潜规则了要有益处. 哈哈~~~本年总结 新年祝福有可能是本年最后一篇下面是我今天博文的部分列表, 先祝大家新的一年快乐!* 程序员人生有些时候作为程序员我们只是需要被重启一下码斗士的修炼之路 -- 如何保持并提升战斗力如何做一个快乐的ASP.NET程序员* C# 语言写出优雅简明代码的论题集 -- Csharp(C#)篇[1]写出优雅简明代码的论题集 -- Csharp(C#)篇[2]再说Csharp(C#) ”整洁代码”那些事 -- 变小[1]C# 中奇妙的函数–8. String Remove() 和 Replace()C# 中奇妙的函数–7. String Split 和 JoinC# 中奇妙的函数–6. 五个序列聚合运算(Sum, Average, Min, Max,Aggregate)C# 中奇妙的函数–5. Nullable 静态类C# 中奇妙的函数 -- 4. Empty, DefaultIfEmpty, CountC# 中奇妙的函数 -- 3. 联接序列的五种简单方法C# 中奇妙的函数 -- 2. First 和 Single -- 你是她心中的第一还是唯一?C# 中奇妙的函数 -- 1. ToLookup不可不知的C#基础 4. 延迟加载 -- 提高性能不可不知的C#基础 3. 线程浅析不可不知的C#基础 2. -–从 struct 和 class的异同 说开去不可不知的C#基础 1. -- Extension 扩展方法

相关新闻

【Bug已解决】Codex CLI 运行时出现 command not found: codex 解决方案
2026/7/5 5:00:51

【Bug已解决】Codex CLI 运行时出现 command not found: codex 解决方案

【Bug已解决】Codex CLI 运行时出现 command not found: codex 解决方案 1. 问题描述 按照文档执行 npm install -g openai/codex 安装完成后,输入 codex 命令却提示找不到: zsh: command not found: codex bash: codex: command not foundWindows Power…

阅读更多
蓝桥杯CTF实战:从爬虫协议到逆向工程的网络安全攻防解析
2026/7/5 5:00:51

蓝桥杯CTF实战:从爬虫协议到逆向工程的网络安全攻防解析

1. 项目概述:蓝桥杯网络安全赛题实战拆解最近几年,蓝桥杯的网络安全赛道热度越来越高,成了很多同学入坑CTF(夺旗赛)和检验自己安全技能的重要舞台。我自己带过不少学生打比赛,也复盘过很多届的真题&#xf…

阅读更多
ServerPackCreator:自动化Minecraft服务器包生成工具的设计哲学与实战指南
2026/7/5 5:00:51

ServerPackCreator:自动化Minecraft服务器包生成工具的设计哲学与实战指南

ServerPackCreator:自动化Minecraft服务器包生成工具的设计哲学与实战指南 【免费下载链接】ServerPackCreator Create a server pack from a Minecraft Forge, NeoForge, Fabric, LegacyFabric or Quilt modpack! 项目地址: https://gitcode.com/gh_mirrors/se/S…

阅读更多
Bilibili-Old开源工具完整指南:如何在3分钟内实现B站界面现代化改造
2026/7/5 6:00:51

Bilibili-Old开源工具完整指南:如何在3分钟内实现B站界面现代化改造

Bilibili-Old开源工具完整指南:如何在3分钟内实现B站界面现代化改造 【免费下载链接】Bilibili-Old 恢复旧版Bilibili页面,为了那些念旧的人。 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-Old 你是否厌倦了B站日益复杂的界面设计&…

阅读更多
GLM 5.2 发布:当长上下文与智能体走向深度融合
2026/7/5 6:00:51

GLM 5.2 发布:当长上下文与智能体走向深度融合

GLM 5.2 发布:当长上下文与智能体走向深度融合 在当今的大模型领域,我们正处于一个微妙的转折点。单纯的参数竞赛似乎已不再是唯一的焦点,开发者们开始更加关注模型在实际工程落地中的表现——尤其是长文本处理能力和复杂任务的自主执行能力。…

阅读更多
Adobe-GenP 3.0:设计师的创意工具箱解锁指南
2026/7/5 6:00:51

Adobe-GenP 3.0:设计师的创意工具箱解锁指南

Adobe-GenP 3.0:设计师的创意工具箱解锁指南 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP 在数字创意领域,Adobe Creative Cloud系列软件无…

阅读更多
如何快速搭建高效开源教务系统:5分钟部署专业级学校管理平台
2026/7/5 6:00:51

如何快速搭建高效开源教务系统:5分钟部署专业级学校管理平台

如何快速搭建高效开源教务系统:5分钟部署专业级学校管理平台 【免费下载链接】schoolcms 中国首个开源学校教务管理系统、网站布局自动化、学生/成绩/教师、成绩查询 项目地址: https://gitcode.com/gh_mirrors/sc/schoolcms 还在为教务管理效率低下而烦恼吗…

阅读更多
基于Agentic AI与提示工程的教育AI自动化批改系统设计与实践
2026/7/5 6:00:51

基于Agentic AI与提示工程的教育AI自动化批改系统设计与实践

1. 项目概述:当AI不只是“助手”,而是“老师”最近和几个做在线教育的朋友聊天,他们都在为一个问题头疼:作业批改。尤其是主观题、作文、编程题,老师批改起来耗时耗力,反馈还不及时。市面上很多所谓的“AI批…

阅读更多
引发事件的问题
2026/7/5 5:00:51

引发事件的问题

引发事件是一个非常容易的事情, 但是的确也有它的误区. 让我们举个例子. 假设我们写个消息接收器, 每当我们收到一个新消息, 我们引发一个包含了新消息的事件 MessageReceived. 安装我们通常的方法,就是: public class MessageReceivedEventArgs : EventArgs {// 接收到的消息…

阅读更多
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御
2026/7/5 0:00:50

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

阅读更多
3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略
2026/7/5 0:00:50

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否曾为Windows右键菜单中那些…

阅读更多
GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复
2026/7/5 0:00:50

GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复

如果你正在用 GXDE OS 或者任何基于 Deepin 的发行版,并且遇到了“检测到窗口系统采用 Wayland 协议,程序即将退出”这类弹窗,或者发现 VMware Tools 在 Ubuntu 24.04 这类默认 Wayland 的系统上启动失败,那这篇文章就是为你准备的…

阅读更多
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御
2026/7/5 0:00:50

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

阅读更多
3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略
2026/7/5 0:00:50

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略

3步彻底解决Windows右键菜单混乱问题:ContextMenuManager使用全攻略 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否曾为Windows右键菜单中那些…

阅读更多
GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复
2026/7/5 0:00:50

GXDE OS下Wayland兼容性实战:从deepin-mutter原理到VMware Tools修复

如果你正在用 GXDE OS 或者任何基于 Deepin 的发行版,并且遇到了“检测到窗口系统采用 Wayland 协议,程序即将退出”这类弹窗,或者发现 VMware Tools 在 Ubuntu 24.04 这类默认 Wayland 的系统上启动失败,那这篇文章就是为你准备的…

阅读更多
基于Dify与DeepSeek构建私有知识库问答系统实战指南
2026/7/4 11:17:16

基于Dify与DeepSeek构建私有知识库问答系统实战指南

在业务中快速构建一个能理解私有文档、准确回答专业问题的智能助手,是很多开发团队面临的共同挑战。传统方案往往需要从零开始搭建复杂的 RAG(检索增强生成)系统,涉及文档解析、向量化、检索、大模型调用等多个环节,整…

阅读更多
FAE放射组学分析工具:医学影像特征探索的完整解决方案
2026/7/4 5:24:16

FAE放射组学分析工具:医学影像特征探索的完整解决方案

FAE放射组学分析工具:医学影像特征探索的完整解决方案 【免费下载链接】FAE FeAture Explorer 项目地址: https://gitcode.com/gh_mirrors/fae/FAE 你是否曾经面对海量医学影像数据感到无从下手?想要从CT、MRI等影像中提取有价值的定量特征&#…

阅读更多
DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!
2026/7/4 15:20:35

DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖!

DesktopNaotu:你的终极离线思维导图解决方案,告别网络依赖! 【免费下载链接】DesktopNaotu 桌面版脑图 (百度脑图离线版,思维导图) 跨平台支持 Windows/Linux/Mac OS. (A cross-platform multilingual Mind Map Tool) 项目地址:…

阅读更多