自定义弱引用实现

发布于 2024-11-29 20:16:48 字数 1508 浏览 0 评论 0原文

BCL 中的 WeakReference 是在前泛型时代设计的,因此它的界面并没有想象中那么好。 IsAlive 属性也很容易被滥用。 通过 Reflector 查看 WeakReference 的实现,似乎我们可以自己实现它。 这是我想出的:

    [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 对应项。 谁能发现上面代码中的任何问题吗?

WeakReference in BCL was designed in pre generics era, so it's interface is not as nice as it could be. Also IsAlive property is very easy to misuse.
Looking into the implementation of WeakReference trough Reflector it seems like we could implement it ourselves.
Here is what I came up with:

    [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;
            }
        }
    }

but I'm not sure I got the implementation of BCL counterpart right.
Can anyone spot any issues in the code above?

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

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

发布评论

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

评论(2

℡Ms空城旧梦 2024-12-06 20:16:48

除了添加错误处理之外,我没有立即发现任何错误。然而,我更喜欢这个实现,因为它简单,特别是因为它使用 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;
}

I don't spot anything wrong offhand except adding error handling. However, I prefer this implementation for its simplicity, especially since it uses the BCL version and you don't have to try as hard to "get it right":

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;
}
挽清梦 2024-12-06 20:16:48
  • 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);
        }
    }
}
  • The GCHandle methods could throw exceptions - so make sure you have your try/catches.
  • You could encourage better usage by providing a TryGetTarget method.
  • Why call it WeakRef?

Here is my go at it.

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