在 C#/.NET 中,有没有办法在弱引用指向的对象被破坏之前获得通知?基本上,我想允许一个对象被收集,但是在对象被销毁之前做一些事情,而不修改代码来添加析构函数(因为我不知道我的代码将使用什么类型的对象)。
谢谢,罗伯特
在 C#/.NET 中,有没有办法在弱引用指向的对象被破坏之前获得通知?基本上,我想允许一个对象被收集,但是在对象被销毁之前做一些事情,而不修改代码来添加析构函数(因为我不知道我的代码将使用什么类型的对象)。
谢谢,罗伯特
.Net 4.0 有你需要的解决方案:ConditionalWeakTable。这是一个演示这个想法的简短程序。(这里也讨论过)
using System;
using System.Runtime.CompilerServices;
namespace GCCollectNotification
{
class ObjectToWatch { }
class Notifier
{
public object ObjectToWatch { get; set; }
~Notifier() { Console.WriteLine("object is collected"); }
}
class Program
{
private ConditionalWeakTable<object, Notifier> map
= new ConditionalWeakTable<object, Notifier>();
public void Test()
{
var obj = new ObjectToWatch();
var notifier = map.GetOrCreateValue(obj);
notifier.ObjectToWatch = obj;
}
static void Main(string[] args)
{
new Program().Test();
GC.Collect();
GC.WaitForPendingFinalizers();
// "object is collected" should have been printed by now
Console.WriteLine("end of program");
}
}
}
不,没有办法实现这个功能。
经过一番猜测,我认为不可能以您描述的方式实现功能。
考虑到在收集 WeakReference 持有的对象时,没有更多的引用(因此它是可收集的)。为了使事件对您有任何用处,它需要提供对象作为事件的一部分。这意味着参考已从可收集变为不可收集。没有什么可以阻止处理代码重新获取对该对象的引用。因此,该对象不再被认为是可收藏的。CLR 需要对对象进行第二次传递以重新确保它是可收集的。
您可以看到第二次无法引发事件,因为它会导致无法收集的对象。
声称此事件是在收集对象之前引发的,这是对命名的滥用。仅仅是因为任何处理程序都可以通过建立对对象的新引用来阻止它被收集。相反,它必须是“ObjectMaybeAboutToBeCollected”。这可能不会给你你正在寻找的行为。
你不能那样做。但是,您可以做的是观察 GC 何时接近(CLR v3.5Sp1 中有新的 GC API 允许您这样做,GCNotifications)
你的问题对我来说没有意义。将要调用的代码应该驻留在哪里?鉴于弱引用将在被引用的对象被销毁之前被清空,因此将它作为引用即将被销毁的对象的类的一部分是没有意义的。并且在对象被销毁之前调用的引用对象中已经有代码 - 这就是析构函数。
您要解决的实际设计问题是什么?可能有更好的方法。
对于您所描述的,终结器将是一种更好的方法。
如果带有通知器的弱引用被视为类似于带有终结器的对象,则可能具有类似于您所描述的语义,也就是说,当对象被认为不再对任何人感兴趣时,它会排队等待最终确定和通知;队列条目将被视为实时引用,因此在对其执行操作之前不会实际收集对象。
由于这是不可能的,最好的可行方法可能是让所有“我对此对象感兴趣”引用指向一个轻量级包装对象,该对象又指向真实对象,并具有“弱”引用指向一个不同的包装器,它也将指向真实的对象。第一个包装器应该包含对第二个包装器的引用,但反之则不然。第一个包装器应该有一个终结器,当它超出范围时将触发适当的代码。
不幸的是,我还没有看到这种策略的任何完整实现。有一些重要的注意事项需要考虑。其中:(1)终结者永远不应该等待锁,也不应该做任何可能抛出异常的事情;(2) 访问可能超出范围的其他对象的代码必须为它们可能已经完成、正在完成、正在等待完成或在其他地方仍有实时引用的可能性做好准备;(3) 如果终结器存储了对已被认为符合垃圾回收条件的可终结对象的根引用,则即使活动引用存在,该对象也可能被终结。