views:

76

answers:

2

A List does not work in the way that I want. The way that I want is that WeakReferences are automatically removed from the list when the object they weakly reference is garbage collected.

ConditionalWeakTable does not do what I want either, because although its keys and values are weakly referenced and collectable, you cannot enumerate them at all.

+1  A: 

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++;
        }
    }
Thomas Levesque
+2  A: 

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.

Stephen Cleary
It's a codeplex project?! Awesome. Thanks. :-)
Tim Lovell-Smith