0

0

C# 弱事件模式实现方法 C#如何避免事件订阅导致的内存泄漏

畫卷琴夢

畫卷琴夢

发布时间:2026-02-05 11:20:01

|

353人浏览过

|

来源于php中文网

原创

事件订阅导致内存泄漏是因为事件源对订阅者的处理方法持有强引用,使订阅者无法被GC回收;WeakEventManager通过弱引用解决此问题,但高频或需强保证的场景不适用。

c# 弱事件模式实现方法 c#如何避免事件订阅导致的内存泄漏

为什么事件订阅会导致内存泄漏

在 C# 中,事件本质上是多播委托(MulticastDelegate),当对象 A 订阅对象 B 的事件时,B 会持有一个指向 A 中处理方法的强引用。只要 B 没被释放,A 就无法被 GC 回收——哪怕 A 的业务逻辑早已结束。典型场景如:UI 控件订阅 ViewModel 事件、后台服务订阅长时间存活对象的事件。

这种泄漏不易察觉,尤其在 WPF/WinForms 中反复创建/销毁视图时,内存占用持续上涨却无明显异常。

WeakEventManager 是最稳妥的内置方案

WeakEventManager 是 WPF 提供的、专为解决 UI 层事件内存泄漏设计的弱事件管理器。它不持有事件处理者的强引用,允许处理者被 GC 正常回收。

使用要点:

  • 必须继承 WeakEventManager 并实现 StartListening/StopListening
  • 事件源(sender)需支持 INotifyPropertyChanged 或自定义事件(如 PropertyChangedCollectionChanged
  • 推荐用泛型静态类封装,避免重复注册:例如 PropertyChangedEventManager.AddHandler
  • WPF 之外(如 .NET Core Console 或 Blazor)默认不可用,需手动引入 PresentationCore 引用

示例(监听 INotifyPropertyChanged):

PropertyChangedEventManager.AddHandler(source, handler, "PropertyName");

触发后若 handler 所属对象已回收,该监听自动失效,不会 crash。

夸克文档
夸克文档

夸克文档智能创作工具,支持AI写作/AIPPT/AI简历/AI搜索等

下载

手动实现 WeakReference + EventHandler 的轻量方案

对非 WPF 环境或需要完全可控的场景,可基于 WeakReference 自建弱事件包装器。核心是:不把 handler 直接存进事件委托链,而是通过弱引用来间接调用。

关键实现细节:

  • WeakReferenceWeakReference + 反射调用,但后者性能差;推荐前者配合闭包捕获
  • 每次触发前必须先 TryGetTarget(out action),为 null 则自动从内部列表移除
  • 事件订阅方法(如 Subscribe)应返回 IDisposable,便于显式清理残留项
  • 避免在 Finalize 或终结器中操作事件,GC 不保证执行时机

简化的订阅结构示意:

public class WeakEvent
{
    private readonly List>> _handlers = new();
    public void Subscribe(object subscriber, Action handler) {
        _handlers.Add(new WeakReference>(handler));
    }
    public void Raise(object sender, TEventArgs e) {
        _handlers.RemoveAll(wr => !wr.TryGetTarget(out var h) || h == null);
        foreach (var wr in _handlers.ToList()) {
            if (wr.TryGetTarget(out var h)) h(sender, e);
        }
    }
}

哪些情况不适合用弱事件

弱事件本质是“放弃对订阅者的生命周期控制”,因此以下场景要格外谨慎:

  • 事件处理逻辑必须严格保证执行(如资源清理、状态同步),而弱引用可能在触发前已被回收
  • 高频触发事件(如鼠标移动、渲染帧回调),频繁 TryGetTarget 和列表遍历带来额外开销
  • 跨线程访问未加锁的弱引用集合,可能引发 InvalidOperationException 或漏触发
  • 处理方法是静态方法或 lambda 闭包捕获了长生命周期对象,导致弱引用失效(实际仍强引用)

真正安全的弱事件,依赖的是「处理者自身可被及时回收」这一前提。如果对象本就该长期存活,那问题不在事件,而在设计本身。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

238

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

560

2024.03.01

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

208

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

191

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

58

2026.01.05

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

589

2023.08.10

go语言闭包相关教程大全
go语言闭包相关教程大全

本专题整合了go语言闭包相关数据,阅读专题下面的文章了解更多相关内容。

137

2025.07.29

console接口是干嘛的
console接口是干嘛的

console接口是一种用于在计算机命令行或浏览器开发工具中输出信息的工具,提供了一种简单的方式来记录和查看应用程序的输出结果和调试信息。本专题为大家提供console接口相关的各种文章、以及下载和课程。

417

2023.08.08

AO3官网入口与镜像站汇总 Archive of Our Own访问路径及最新入口
AO3官网入口与镜像站汇总 Archive of Our Own访问路径及最新入口

本专题专注于提供Archive of Our Own (AO3) 的最新官网入口与镜像站地址,详细整理了可用的访问路径,包括中文镜像站入口和网页版直达链接,帮助用户轻松找到最稳定的访问方式,确保顺畅浏览AO3内容。

1

2026.02.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号