Ответ 1
Я согласен, что реализация WeakList<T>
возможна, но я не думаю, что это просто. Вы можете использовать мою реализацию здесь. Класс WeakCollection<T>
зависит от WeakReference<T>
, который, в свою очередь, зависит от SafeGCHandle
.
Использование List<WeakReference>
не будет работать так, как я хочу. Я хочу, чтобы WeakReferences автоматически удалялся из списка, когда объект, с которым они ссылаются, собирает мусор.
ConditionalWeakTable<TKey,TValue>
тоже не удовлетворяет меня, потому что, хотя его ключи и значения слабо ссылаются и собираются, вы не можете их перечислять!
Я согласен, что реализация WeakList<T>
возможна, но я не думаю, что это просто. Вы можете использовать мою реализацию здесь. Класс WeakCollection<T>
зависит от WeakReference<T>
, который, в свою очередь, зависит от SafeGCHandle
.
Вы можете легко реализовать класс WeakList<T>
, который бы обернул List<WeakReference>
.
Нет способа автоматического удаления объектов при сборе мусора, поскольку невозможно обнаружить, когда это произойдет. Тем не менее, вы можете удалить "мертвые" (собранные мусором) объекты, когда вы их встретите, проверив свойство WeakReference.IsAlive
. Однако я бы не рекомендовал этот подход, потому что это может привести к запутанному поведению с клиентской точки зрения. Вместо этого я бы рекомендовал внедрить метод Purge
для удаления мертвых записей, которые вы бы назвали явно.
Здесь пример реализации:
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);
}
}
Этот класс использует следующие классы и методы расширения:
WeakReference<T>
(только строго типизированная оболочка вокруг 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
(так же, как IList<T>.IndexOf
, но работает на 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
(так же, как IList<T>.CopyTo
, но работает на 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++;
}
}
Для всех, кому необходимо использовать ConditionalWeakTable в .NET 2.0 или 3.5, здесь есть обратный путь: https://github.com/theraot/Theraot/wiki/Features