You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET5.0 DEBUG模式下WeakReference相关功能如何实现可靠单元测试

可行测试方案及原理

问题根因

Debug模式下JIT会延长方法局部变量的生命周期,会将所有局部变量的GC根保留到方法执行结束,哪怕你手动将变量赋值为null,JIT仍然认为该变量处于有效作用域内,不会释放根引用,因此GC无法回收目标对象,这就是你原有测试代码在net5.0 Debug下始终失败的核心原因。

核心解决思路

将需要被GC回收的目标对象的强引用,放到独立的、不会被内联的方法作用域中创建,等该方法执行完成后,对应栈帧销毁,所有局部强引用会被自动清除,此时再触发GC就能稳定回收目标对象。

具体测试实现

1. 测试OnFieldUpdated正常转发逻辑

这个场景不需要触发GC,直接验证事件转发逻辑即可:

[Fact]
public void OnFieldUpdated_Should_Forward_To_Listener_When_Alive()
{
    // 准备测试数据
    var entity = new Entity();
    var mockListener = new Mock<IEntityListener>();
    _ = new WeakEntityListener(entity, mockListener.Object);
    var eventArgs = new FieldUpdatedEventArgs();

    // 触发事件,若事件为内部成员可通过反射触发,或给原项目添加InternalsVisibleTo特性开放给测试项目
    entity.RaiseFieldUpdated(eventArgs);

    // 验证转发成功
    mockListener.Verify(l => l.OnFieldUpdated(eventArgs), Times.Once);
}

2. 测试CleanUp自动清理逻辑

这个场景需要控制作用域确保监听器可被回收:

// 禁止方法内联,确保作用域独立,方法执行完后局部强引用全部销毁
[MethodImpl(MethodImplOptions.NoInlining)]
private static (Entity Entity, WeakReference<IEntityListener> WeakListener) CreateTestObjects()
{
    var entity = new Entity();
    var listener = new Mock<IEntityListener>().Object;
    _ = new WeakEntityListener(entity, listener);
    return (entity, new WeakReference<IEntityListener>(listener));
}

[Fact]
public void OnFieldUpdated_Should_CleanUp_When_Listener_Collected()
{
    // 从独立方法获取对象,此时CreateTestObjects的栈帧已销毁,不存在listener的强引用
    var (entity, weakListener) = CreateTestObjects();

    // 强制全代GC回收,覆盖所有代和大对象堆
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
    GC.WaitForPendingFinalizers();
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);

    // 验证监听器已被回收
    Assert.False(weakListener.TryGetTarget(out _));

    // 触发事件,此时会执行CleanUp逻辑移除事件订阅
    entity.RaiseFieldUpdated(new FieldUpdatedEventArgs());

    // 验证事件委托已被清空,可直接判断事件或通过反射获取调用列表验证
    Assert.Null(entity.FieldUpdated);
}

额外兼容配置

如果需要兼容所有框架版本和编译模式,可以在测试项目的.csproj中添加如下配置,无需关闭分层编译:

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
  <GCStressLevel>3</GCStressLevel>
</PropertyGroup>

该配置会让Debug模式下GC的回收行为更激进,进一步保证测试稳定性。

内容的提问来源于stack exchange,提问作者Weifen Luo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 23:15:07