Is there a way to do a WeakList or WeakCollection

2019-06-14 23:21发布

Using a List<WeakReference> will not work as I want. What I want is for WeakReferences to be automatically removed from the list whenever the object they reference is garbage collected.

ConditionalWeakTable<TKey,TValue> does not satisfy me either, because although its keys and values are weakly referenced and collectable, you cannot enumerate them!

3条回答
祖国的老花朵
2楼-- · 2019-06-15 00:06

You could easily implement a WeakList<T> class, which would wrap a List<WeakReference>.

There is no way to automatically remove objects when they are garbage collected, because it's not possible to detect when this happens. However, you could remove "dead" (garbage collected) objects when you encounter them, by checking the WeakReference.IsAlive property. However, I wouldn't recommend this approach, because it could lead to confusing behavior from the client's point of view. Instead, I would recommend implementing a Purge method to remove dead entries, that you would call explicitly.

Here's a sample implementation :

public class WeakList<T> : IList<T>
{
    private List<WeakReference<T>> _innerList = new List<WeakReference<T>>();

    #region IList<T> Members

    public int IndexOf(T item)
    {
        return _innerList.Select(wr => wr.Target).IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        _innerList.Insert(index, new WeakReference<T>(item));
    }

    public void RemoveAt(int index)
    {
        _innerList.RemoveAt(index);
    }

    public T this[int index]
    {
        get
        {
            return _innerList[index].Target;
        }
        set
        {
            _innerList[index] = new WeakReference<T>(value);
        }
    }

    #endregion

    #region ICollection<T> Members

    public void Add(T item)
    {
        _innerList.Add(new WeakReference<T>(item));
    }

    public void Clear()
    {
        _innerList.Clear();
    }

    public bool Contains(T item)
    {
        return _innerList.Any(wr => object.Equals(wr.Target, item));
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        _innerList.Select(wr => wr.Target).CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _innerList.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        int index = IndexOf(item);
        if (index > -1)
        {
            RemoveAt(index);
            return true;
        }
        return false;
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return _innerList.Select(x => x.Target).GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    #endregion

    public void Purge()
    {
        _innerList.RemoveAll(wr => !wr.IsAlive);
    }
}

This class uses the following classes and extension methods :

WeakReference<T> (just a strongly typed wrapper around WeakReference)

[Serializable]
public class WeakReference<T> : WeakReference
{
    public WeakReference(T target)
        : base(target)
    {
    }

    public WeakReference(T target, bool trackResurrection)
        : base(target, trackResurrection)
    {
    }

    public WeakReference(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
    }

    public new T Target
    {
        get
        {
            return (T)base.Target;
        }
    }
}

IndexOf (same as IList<T>.IndexOf, but works on a IEnumerable<T>)

    public static int IndexOf<T>(this IEnumerable<T> source, T item)
    {
        var entry = source.Select((x, i) => new { Value = x, Index = i })
                    .Where(x => object.Equals(x.Value, item))
                    .FirstOrDefault();
        return entry != null ? entry.Index : -1;
    }

CopyTo (same as IList<T>.CopyTo, but works on a IEnumerable<T>)

    public static void CopyTo<T>(this IEnumerable<T> source, T[] array, int startIndex)
    {
        int lowerBound = array.GetLowerBound(0);
        int upperBound = array.GetUpperBound(0);
        if (startIndex < lowerBound)
            throw new ArgumentOutOfRangeException("startIndex", "The start index must be greater than or equal to the array lower bound");
        if (startIndex > upperBound)
            throw new ArgumentOutOfRangeException("startIndex", "The start index must be less than or equal to the array upper bound");

        int i = 0;
        foreach (var item in source)
        {
            if (startIndex + i > upperBound)
                throw new ArgumentException("The array capacity is insufficient to copy all items from the source sequence");
            array[startIndex + i] = item;
            i++;
        }
    }
查看更多
叛逆
3楼-- · 2019-06-15 00:07

For anybody needing to use a ConditionalWeakTable in .NET 2.0 or 3.5 there is a backport of it here: https://github.com/theraot/Theraot/wiki/Features

查看更多
闹够了就滚
4楼-- · 2019-06-15 00:19

I agree that implementing a WeakList<T> is possible, but I don't think it's exactly easy. You're welcome to use my implementation here. The WeakCollection<T> class depends on WeakReference<T>, which in turn depends on SafeGCHandle.

查看更多
登录 后发表回答