C#中为DisplaySettingsChanged事件添加Lambda表达式会导致内存泄漏吗?
结论
直接使用匿名Lambda表达式绑定SystemEvents.DisplaySettingsChanged事件且不手动卸载的写法不可行,一定会造成内存泄漏,不能用来替代原有带+=/-=的命名方法注册方案。
原因说明
SystemEvents类的所有事件都是静态事件,其生命周期和整个应用程序域保持一致,只要注册了事件处理器,静态事件实例就会一直持有该处理器的引用,直到程序退出或者手动卸载处理器。- 匿名Lambda表达式没有可复用的引用标识,你无法在后续通过
-=操作符匹配到之前注册的Lambda实例,哪怕你写一个逻辑完全一致的Lambda去执行卸载,也只会生成一个新的委托实例,无法卸载之前注册的处理器。 - 对于你提供的
CompositionMetrics场景,Lambda内部必然会捕获当前类的实例成员(比如hwndSource、属性字段等),这会导致静态事件直接持有整个CompositionMetrics实例的引用,就算你调用了Dispose方法,GC也无法回收该实例,直接产生内存泄漏。同时已经被标记释放的实例被事件回调时,还可能触发不可预期的运行时异常。
可选的Lambda兼容写法(不推荐)
如果你非要用Lambda写法,必须将Lambda委托存为类的字段,才能实现后续的卸载,写法如下:
public sealed class CompositionMetrics : IDisposable { private readonly HwndSource hwndSource; private readonly EventHandler _displaySettingsChangedHandler; public CompositionMetrics(HwndSource source) { hwndSource = source; _displaySettingsChangedHandler = (_, _) => { // 你的逻辑代码 }; SystemEvents.DisplaySettingsChanged += _displaySettingsChangedHandler; } public void Dispose() { SystemEvents.DisplaySettingsChanged -= _displaySettingsChangedHandler; } // 其余属性、方法代码省略 }
这种写法和原有命名方法的方案没有本质区别,没有额外收益,因此更推荐你保留原有的命名方法注册事件的写法,可读性和可维护性更好。
内容的提问来源于stack exchange,提问作者Selastin
相关产品推荐
相关产品推荐

