C#:收集 WeakReference 之前的通知?

发布于 2024-07-27 18:37:38 字数 134 浏览 5 评论 0原文

在C#/.NET中,有什么方法可以在弱引用指向的对象被破坏之前获得通知吗? 基本上,我想允许收集一个对象,但在对象被销毁之前做一些事情,而不修改代码来添加析构函数(因为我不知道我的代码将起诉什么类型的对象)。

谢谢, 罗伯特

In C#/.NET, is there any way to get a notification before the object pointed to by a weak reference is destructed? Basically, I want to allow an object to be collected, but do something right before the object is destroyed, without modifying code to add destructors (since I won't know exactly what types of objects will be sued with my code).

Thanks,
Robert

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

故事灯 2024-08-03 18:37:39

.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");
        }
    }
}

.Net 4.0 has the solution you need: ConditionalWeakTable. Here is a short program that demonstrates the idea. (discussed here as well)

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");
        }
    }
}
装迷糊 2024-08-03 18:37:39

不,没有办法实现这个功能。

经过一番猜测后,我认为不可能以您所描述的方式实现功能。

考虑到 WeakReference 所持有的对象被收集时,不再有引用(因此它是可收集的)。 为了使事件对您有用,需要提供对象作为事件的一部分。 这意味着参考文献已从可收藏变为不可收藏。 没有什么可以阻止处理代码重新获取该对象的引用。 因此,该物品不再被视为具有收藏价值。 CLR 需要对对象进行第二次传递以重新确保它是可收集的。

您可以看到第二次无法引发该事件,因为它会导致无法收集的对象。

声称此事件是在收集对象之前引发的,这将是对命名的滥用。 仅仅是因为任何处理程序都可以通过建立对该对象的新引用来阻止收集该对象。 相反,它必须是“ObjectMaybeAboutToBeCollected”。 这可能不会给你你正在寻找的行为。

No there is no way to achieve this functionality.

After a bit of speculation, I don't believe it's possible to implement a feature in the manner you are describing.

Consider that at the point the object held by a WeakReference is collected, there are no more references (hence it's collectable). For an event to have any use to you it would need to provide the object as part of the event. This means that the reference has gone from collectable to not-collectable. There is nothing stopping the handling code from retaking a reference on that object. Hence the object can no longer be considered collectable. The CLR would need to make a second pass on the object to re-ensure it was collectable.

You can see how the second time around the event could not be raised because it would lead to uncollectable objects.

It would be a misuse of naming to claim this event was raised just before an object was collected. Simply because any handler could prevent this from being collected by establishing a new reference to the object. Instead it would have to be "ObjectMaybeAboutToBeCollected". This probably won't give you the behavior you're looking for.

栖竹 2024-08-03 18:37:39

你不能那样做。
然而,您可以做的是观察 GC 何时接近(CLR v3.5Sp1 中有新的 GC API 允许您这样做,GCNotifications)

You can't do that.
However what you can do is to watch when a GC is approaching ( there are new GC APIs in CLR v3.5Sp1 that allows you to do so, GCNotifications )

我的奇迹 2024-08-03 18:37:39

你的问题对我来说没有意义。 将要调用的代码应该驻留在哪里? 鉴于弱引用在被引用的对象被销毁之前将被清空,因此它成为引用即将被销毁的对象的类的一部分是没有意义的。 引用的对象中已经有在对象被销毁之前调用的代码 - 这就是析构函数。

您想要解决的实际设计问题是什么? 可能有更好的方法。

Your question doesn't make sense to me. Where is the code that's going to be called supposed to reside? Given that the weak references will be nulled before the referenced object is destroyed, it doesn't make sense for it to be part of the class that referenced the about-to-be destroyed object. And there's already code in the referenced object that's called before the object is destroyed - that's the destructor.

What's the actual design problem you want to solve? There may be a better way.

一口甜 2024-08-03 18:37:39

对于您所描述的,终结器将是一种更好的方法。

For what you are describing, finalizers would be a way better approach.

习惯成性 2024-08-03 18:37:39

如果带有通知程序的弱引用被视为类似于带有终结器的对象,那么就有可能具有与您所描述的类似的语义,也就是说,当该对象被认为不再对任何人感兴趣时,它将排队等候最终确定和通知; 队列条目将被视为实时引用,因此在对其执行操作之前不会真正收集该对象。

由于这是不可能的,最好的可行方法可能是让所有“我对此对象感兴趣”引用指向一个轻量级包装对象,该对象又指向真实对象,并具有“弱”引用指向一个不同的包装器,它也将指向真实的对象。 第一个包装器应该保存对第二个包装器的引用,但反之则不然。 第一个包装器应该有一个终结器,当它超出范围时,它将触发适当的代码。

不幸的是,我还没有看到这种策略的任何完整实施。 有一些重要的注意事项需要考虑。 其中:(1)终结器永远不应该等待锁,也不应该做任何可能引发异常的事情; (2) 访问可能超出范围的其他对象的代码必须做好准备,以应对它们可能已经被最终确定、正在最终确定、正在等待最终确定或在其他地方仍然有活动引用的可能性; (3) 如果终结器存储对已发现符合垃圾回收条件的可终结对象的根引用,则即使存在实时引用,此类对象也可能被终结。

It would be possible to have semantics similar to what you describe if a weak reference with a notifier were regarded similarly to an object with a finalizer, which is to say that when the object was considered to no longer be of interest to anybody, it would be queued for finalization and notification; the queue entry would be considered a live reference, so the object would not actually be collected until it was acted upon.

Since that isn't possible, the best feasible approach might probably be to have all "I'm interested in this object" references point to a lightweight wrapper object which would in turn point to the real object, and have the "weak" references point to a different wrapper which would also point to the real object. The first wrapper should hold a reference to the second, but not vice versa. The first wrapper should have a finalizer which will trigger appropriate code when it goes out of scope.

Unfortunately, I haven't seen any complete implementations of such a strategy. There are some important caveats to consider. Among them: (1) finalizers should never wait on locks, nor do anything that could throw an exception; (2) code which accesses other objects which might have gone out of scope must be prepared for the possibility that they may have already been finalized, be in the process of being finalized, be awaiting finalization, or still have live references elsewhere; (3) if a finalizer stores a rooted reference to a finalizable object which has been found eligible for garbage collection, such an object may be finalized even though the live reference exists.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文