【发布时间】:2021-12-31 17:11:59
【问题描述】:
似乎以下用于单元测试WeakReference 的代码无法正常/可靠地工作:
object realObject = new object();
WeakReference weakReference = new WeakReference(realObject);
Assert.NotNull(weakReference.Target);
realObject = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Assert.Null(weakReference.Target);
测试针对 net461 和 net5.0 运行,适用于 DEBUG 和 RELEASE 模式。最好的结果:
-
以 net461 为目标:以上代码运行成功,无论是 DEBUG 还是 RELEASE 模式。
-
以 net5.0 为目标:上面的代码一开始在 DEBUG 和 RELEASE 模式下都失败了。阅读这篇文章 (Garbage collection behaviour difference between .NetFramework 4.8 and .Net 5) 后,它可以通过将
<TieredCompilation>false</TieredCompilation>添加到.csproj以在 RELEASE 模式下运行。
所以问题缩小到针对 net5.0 调试模式。
我也看到了以下帖子,但它们似乎不是很有帮助,主要是因为它们已经过时了:
值得一提的是@Eric Lippert 对Why C# Garbage Collection behavior differs for Release and Debug executables? 的回答说:您绝对不能依赖垃圾收集器在生命周期方面具有任何特定行为本地的,这表明无法可靠地测试 WeakReference。
编辑:有关为什么使用WeakReference 的上下文的更多信息:
我正在开发一个框架,提供类似于System.Data.DataRow 和System.Data.DataRowView 的类Entity<T> 和Record<T>,最重要的是前一个是强类型的。实体是模型提供自己的事件来通知更改,Record<T> 是一个视图模型,它包装了Entity<T> 以将更改事件转换为INotifyPropertyChanged 以及其他内容,例如数据绑定、验证等。Entity<T> 不应该知道Record<T> 的存在。为避免内存泄漏,Record<T> 应在仅 Entity 仍被引用时进行 GC 回收。
事件连接的示例代码:
IEntityListener由Record<T>类实现:
internal interface IEntityListener
{
void OnFieldUpdated(FieldUpdatedEventArgs e);
}
WeakEntityListner 进行事件连接:
internal sealed class WeakEntityListener
{
private readonly WeakReference<IEntityListener> _weakListener;
private readonly Entity _entity;
internal WeakEntityListener(Entity entity, IEntityListener listener)
{
_entity = entity;
_weakListener = new (listener);
_entity.FieldUpdated += OnFieldUpdated;
}
private void OnFieldUpdated(object? sender, FieldUpdatedEventArgs e)
{
if (_weakListener.TryGetTarget(out var listener))
listener.OnFieldUpdated(e);
else
CleanUp();
}
private void CleanUp()
{
_entity.FieldUpdated -= OnFieldUpdated;
}
}
我希望方法 OnFieldUpdated 和 CleanUp 被单元测试完全覆盖。
【问题讨论】:
-
恕我直言,您似乎已经回答了自己的问题。但是为什么你要测试弱引用的行为呢?您的应用程序依赖于什么行为?
-
实际场景是使用弱引用的自定义弱事件实现。我需要测试当侦听器被垃圾收集时事件处理程序将被取消连接。这个问题没有完全回答,因为测试代码在调试模式下无法针对 net5.0 工作。
-
使测试用例完全确定可能是不可能的。即使github.com/dotnet/runtime/blob/main/src/libraries/… 似乎也只有在
PlatformDetection.IsPreciseGcSupported时才会运行。我会退一步问你为什么使用弱引用,没有它们你能实现你的要求吗? -
WeakReference 是我所知道的实现弱事件的唯一实用选项:codeproject.com/Articles/29922/Weak-Events-in-C。基本上在 CLR 世界中,WeakReference 是允许引用对象同时仍允许垃圾收集回收该对象的唯一选项。如果我错了,请纠正我。
-
@DekuDesu 我已经用事件连接示例更新了问题。
标签: c# .net .net-core garbage-collection weak-references