29

Implementing PhantomReference in C# [Snippet]

 5 years ago
source link: https://www.tuicool.com/articles/hit/RFNBBvE
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

I ran into this very interesting blog post and I decided to see if I could implement this on my own, without requiring any runtime support. This turned out the be surprisingly easy if you are willing to accept some caveats.

I’m going to assume that you have read the linked blog post, and here is the code that implements it:

public class PhantomReferenceQueue<THandle>
{
    private BlockingCollection<THandle> _queue = new BlockingCollection<THandle>();
    private ConditionalWeakTable<object, PhatomReference> _refs = new ConditionalWeakTable<object, PhatomReference>();

    public void Register(object instance, THandle handle)
    {
        _refs.Add(instance, new PhantomReference(this, handle));
    }

    public void Unregister(object instance)
    {
        if (_refs.TryGetValue(instance, out var val))
            GC.SuppressFinalize(val);
        _refs.Remove(instance);
    }

    public THandle Poll()
    {
        return _queue.Take();
    }

    private class PhantomReference
    {
        THandle _handle;
        PhantomReferenceQueue<THandle> _parent;

        public PhantomReference(PhantomReferenceQueue<THandle> parent, THandle handle)
        {
            _parent = parent;
            _handle = handle;
        }

        ~PhantomReference()
        {
            _parent._queue.Add(_handle);
        }
    }
}

Here is what it gives you:

  • You can have any number of queues.
  • You can associate an instance with a queue at any time, including far after it was constructed.
  • Can unregister from the queue at any time.
  • Can wait (or easily change it to do async awaits, of course) for updates about phantom references.
  • Can process such events in parallel.

What about the caveats?

This utilizes the finalizer internally, to inform us when the associated value has been forgotten, so it takes longer than one would wish for. This implementation relies on ConditionalWeakTable to do its work, by creating a weak associating between the instances you pass and the  PhantomReference   class holding the handle that we’ll send to you once that value has been forgotten.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK