Puede implementar fácilmente una clase WeakList<T>
, que envolvería un List<WeakReference>
.
No hay manera de eliminar objetos automáticamente cuando son basura recolectada, porque no es posible detectar cuándo sucede esto. Sin embargo, puede eliminar los objetos "muertos" (recolección de basura) cuando los encuentre, marcando la propiedad WeakReference.IsAlive
. Sin embargo, no recomendaría este enfoque, ya que podría conducir a un comportamiento confuso desde el punto de vista del cliente. En cambio, recomendaría implementar un método Purge
para eliminar entradas muertas, que llamaría explícitamente.
Aquí está un ejemplo de implementación:
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);
}
}
Esta clase utiliza las siguientes clases y métodos de extensión:
WeakReference<T>
(sólo un envoltorio inflexible en torno 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
(igual que IList<T>.IndexOf
, pero trabaja en un 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
(mismo que IList<T>.CopyTo
, pero trabaja en un 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++;
}
}
interesante ver dos respuestas hasta el momento ambos tienen cierta etapa de purga no-totalmente automática. Necesito pasar algún tiempo pensando en ello, pero en realidad podría ser lo suficientemente bueno para lo que necesito, incluso si no es completamente automático. –
La depuración se realiza de forma más natural durante la enumeración. La única otra opción es tener un purgado periódico, en cuyo caso la solución se convierte más en un "caché" que en una "Lista débil". 'WeakReference' no debe usarse para el almacenamiento en caché; hay mejores soluciones para eso (por ejemplo, [System.Runtime.Caching] (http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx)). –
Gracias por la interesante sugerencia sobre System.Runtime.Caching. Pero tenía una aplicación particular en mente para esta pregunta, y puedo ver algunos desajustes de impedancia: 1) No necesito o no quiero utilizar claves de cadena para obtener elementos, solo quiero poder iterarlos a pedido.2) Probablemente sería más feliz si los elementos solo salen de la memoria caché debido a la recolección de basura, y no por otros motivos diversos (como la memoria caché que usa demasiada memoria). –