It's possible under .NET 4.0 and following using ConditionalWeakTable<TKey, TValue>
. Thanks this, and other sites. It follows proof of concept code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
namespace Test
{
public static class GCInterceptor
{
private static ConditionalWeakTable<object, CallbackRef> _table;
static GCInterceptor()
{
_table = new ConditionalWeakTable<object, CallbackRef>();
}
public static void RegisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (found)
{
callbackRef.Collected += action;
return;
}
int hashCode = RuntimeHelpers.GetHashCode(obj);
callbackRef = new CallbackRef(hashCode);
callbackRef.Collected += action;
_table.Add(obj, callbackRef);
}
public static void DeregisterGCEvent(this object obj, Action<int> action)
{
CallbackRef callbackRef;
bool found = _table.TryGetValue(obj, out callbackRef);
if (!found)
throw new Exception("No events registered");
callbackRef.Collected -= action;
}
private class CallbackRef
{
private int _hashCode;
public event Action<int> Collected;
public CallbackRef(int hashCode)
{
_hashCode = hashCode;
}
~CallbackRef()
{
Action<int> handle = Collected;
if (handle != null)
handle(_hashCode);
}
}
}
}
Tested with the following code:
public partial class Form1 : Form
{
private object _obj;
public Form1()
{
InitializeComponent();
_obj = new object();
_obj.RegisterGCEvent(delegate(int hashCode)
{
MessageBox.Show("Object with hash code " + hashCode + " recently collected");
});
}
private void button1_Click(object sender, EventArgs e)
{
_obj = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Guid
<->WeakReference
. Without this, I can just check that the objects are collected by manually testing the weak references at a deferred time. Objects implements an interface so the framework is more flexible, so adding a finalizer is not an option. – Paperweight