11

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

Robert Fraser
  • 10,649
  • 8
  • 69
  • 93

6 Answers6

11

.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");
        }
    }
}
Reuven Bass
  • 672
  • 1
  • 6
  • 16
6

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.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • Cool, thanks... can you think of any ways to get similar functionality other than WeakReferences? – Robert Fraser Jul 17 '09 at 20:39
  • @Robert, other than form of destructors / dispose I can't think of anything off the top of my head – JaredPar Jul 17 '09 at 20:40
  • I absolutely agree, anyway, weak events might do the trick! One tiny catch, they don't exist in C# so you must implement it (it's a little tricky but basically possible). – Pz. Dec 12 '10 at 12:16
6

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 )

mfawzymkh
  • 4,030
  • 1
  • 16
  • 12
0

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.

Mark Bessey
  • 19,598
  • 4
  • 47
  • 69
  • I want to create a system where objects are registered to be serialized to server occasionally. The serialized data will be pushed to the server every few minutes (out of my control), so usually the serialized data for all registered objects is calculated then. However, if a registered object is being destructed, I want to calculate the data right before it dies (and that data will be sent with the next batch to the server). – Robert Fraser Jul 17 '09 at 21:03
  • 2
    That still seems a little funky. If you're going to be sending the serialized data up to a server periodically, why not just have the object that's responsible for the serialization maintain a (nonweak) reference to the registered objects, and release them once they've been uploaded? Given the unpredictability of the destruction timing, you wouldn't want to depend on it for data integrity, anyway. – Mark Bessey Jul 17 '09 at 22:03
  • They need to keep being serialized every so often as long as they're alive/changing. It's Silverlight UI preferences (i.e. column widths, etc.), so as long as the UI elements are still around, their state needs to be saved on the server. – Robert Fraser Jul 17 '09 at 22:08
0

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

0

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.

supercat
  • 77,689
  • 9
  • 166
  • 211