自定义WeakReference实现

时间:2011-08-17 03:38:30

标签: c# .net garbage-collection

BCL中的WeakReference是在仿制药时代设计的,所以它的界面并不尽如人意。 IsAlive属性也很容易被误用。 查看WeakReference槽Reflector的实现,我们似乎可以自己实现它。 以下是我提出的建议:

    [SecurityPermission(Flags = SecurityPermissionFlag.UnmanagedCode)]
    public sealed class WeakRef<T> where T : class
    {
        private readonly volatile IntPtr _ptr;

        public WeakRef(T target)
            : this(target, false)
        {
        }

        [SecuritySafeCritical]
        public WeakRef(T target, bool trackResurrection)
        {
            var handle = GCHandle.Alloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
            _ptr = GCHandle.ToIntPtr(handle);
        }

        [SecuritySafeCritical]
        ~WeakRef()
        {
            var ptr = _ptr;
            if ((ptr != IntPtr.Zero) && (ptr == Interlocked.CompareExchange(ref _ptr, IntPtr.Zero, ptr)))
            {
                var handle = GCHandle.FromIntPtr(ptr);
                handle.Free();
            }
        }

        public T Target
        {
            get
            {
                var ptr = _ptr;
                if (IntPtr.Zero != ptr)
                {
                    var target = GCHandle.FromIntPtr(ptr).Target;
                    if (_ptr != IntPtr.Zero)
                    {
                        return (T)target;
                    }
                }
                return null;
            }
        }
    }

但我不确定我是否正确实施了BCL对手。 任何人都可以在上面的代码中发现任何问题吗?

2 个答案:

答案 0 :(得分:6)

除了添加错误处理之外,我没有发现任何错误。但是,我更喜欢这个实现的简单性,特别是因为它使用BCL版本而你不必努力“正确”:

public sealed class WeakReference<T> where T : class
{
    public WeakReference(T target) : this(target, trackResurrection)
    {}

    public WeakReference(T target, bool trackResurrection)
    {
        refTarget = new WeakReference(target, trackResurrection);
    }

    public T Target { get { return refTarget.Target as T; } }

    public bool IsAlive { get { return refTarget.IsAlive; }}

    private readonly WeakReference refTarget;
}

答案 1 :(得分:0)

  • GCHandle方法可能会引发异常 - 因此请确保您拥有try / catches
  • 您可以通过提供TryGetTarget方法来鼓励更好地使用。
  • 为什么称它为WeakRef

这是我的目标。

public sealed class WeakReference<T> : IDisposable
    where T : class
{
    private volatile IntPtr _handle;
    private GCHandleType _handleType;

    public WeakReference(T target)
        : this(target, false)
    {
    }

    [SecuritySafeCritical]
    public WeakReference(T target, bool trackResurrection)
    {
        if (target == null)
            throw new ArgumentNullException("target");
        _handleType = trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak;
        Target = target;
    }

    [SecuritySafeCritical]
    ~WeakReference()
    {
        Dispose();
    }

    public void Dispose()
    {
        var ptr = _handle;
        if ((ptr != IntPtr.Zero) &&
            Interlocked.CompareExchange(ref _handle, IntPtr.Zero, ptr) == ptr)
        {
            try
            {
                var handle = GCHandle.FromIntPtr(ptr);
                if (handle.IsAllocated)
                    handle.Free();
            }
            catch
            { }
        }
        GC.SuppressFinalize(this);
    }

    public bool TryGetTarget(out T target)
    {
        var ptr = _handle;
        if (ptr != IntPtr.Zero)
        {
            try
            {
                var handle = GCHandle.FromIntPtr(ptr);
                if (handle.IsAllocated)
                {
                    target = (T)handle.Target;
                    return !object.ReferenceEquals(target, null);
                }
            }
            catch
            { }
        }
        target = null;
        return false;
    }

    public bool TryGetTarget(out T target, Func<T> recreator)
    {
        IntPtr ptr = _handle;
        try
        {
            var handle = GCHandle.FromIntPtr(ptr);
            if (handle.IsAllocated)
            {
                target = (T)handle.Target;
                if (!object.ReferenceEquals(target, null))
                    return false;
            }
        }
        catch
        { }

        T createdValue = null;
        target = null;

        while ((ptr = _handle) == IntPtr.Zero || object.ReferenceEquals(target, null))
        {
            createdValue = createdValue ?? recreator();
            var newPointer = GCHandle.Alloc(createdValue, _handleType).AddrOfPinnedObject();
            if (Interlocked.CompareExchange(ref _handle, newPointer, ptr) == ptr)
            {
                target = createdValue;
                return true;
            }
            else if ((ptr = _handle) != IntPtr.Zero)
            {
                try
                {
                    var handle = GCHandle.FromIntPtr(ptr);
                    if (handle.IsAllocated)
                    {
                        target = (T)handle.Target;
                        if (!object.ReferenceEquals(target, null))
                            return false;
                    }
                }
                catch
                { }
            }
        }

        return false;
    }

    public bool IsAlive
    {
        get
        {
            var ptr = _handle;
            return ptr != IntPtr.Zero && GCHandle.FromIntPtr(ptr).IsAllocated;
        }
    }

    public T Target
    {
        get
        {
            T target;
            TryGetTarget(out target);
            return target;
        }
        set
        {
            Dispose();
            _handle = GCHandle.Alloc(value, _handleType).AddrOfPinnedObject();
            GC.ReRegisterForFinalize(this);
        }
    }
}