2010-05-14 5 views
14

El uso de un List<WeakReference> no funcionará como yo quiera. Lo que quiero es que WeakReferences se elimine automáticamente de la lista cuando el objeto al que hacen referencia es basura.¿Hay alguna manera de hacer una WeakList o WeakCollection (como WeakReference) en CLR?

ConditionalWeakTable<TKey,TValue> tampoco me satisface, porque aunque sus claves y valores son débilmente referenciados y coleccionables, ¡no puede enumerarlos!

+0

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. –

+1

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)). –

+0

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). –

Respuesta

7

Estoy de acuerdo que la implementación de un WeakList<T> es posible, pero No creo que sea exactamente fácil. Le invitamos a utilizar mi implementación here. La clase WeakCollection<T> depende de WeakReference<T>, que a su vez depende de SafeGCHandle.

+0

Es un proyecto Codeplex ?! Increíble. Gracias. :-) –

+0

@ stephen-cleary: esto no parece estar en su fuente más reciente, por lo que tengo curiosidad sobre cómo se está acercando a este problema con la última CLR. –

+2

@ Mike-EEE: Para ephemerons, uso [Connected Properties] (http://connectedproperties.codeplex.com/). No son compatibles con la enumeración, pero nunca he necesitado esa capacidad. –

6

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++; 
     } 
    } 
+2

En .net 4.0 sería posible diseñar una estructura de lista que elimine automáticamente los objetos que son GC mediante el uso de 'ConditionalWeakTable' para unir los objetos de la lista a otros objetos con finalizadores que realizarían la eliminación. Tenga en cuenta que esto no se debe hacer con una lista indexada numéricamente (ya que no hay manera de lograr la eliminación en modo thread-safe) pero podría hacerse con una lista enlazada que itere las cosas en orden o en orden inverso a la creación. Sin embargo, no estoy seguro de en qué casos eliminar activamente las referencias cuando se vuelvan muertas ... – supercat

+0

... sería mejor que contar cuántos elementos se han agregado desde la última purga y cuántos estaban vivos en ese momento, y haciendo una purga cuando el número de elementos agregados desde el último excede el número que estaba vivo en ese momento (o, alternativamente, cada vez que se agrega un elemento, escanea algunos elementos para eliminarlos, haciendo un seguimiento de la posición de uno en el lista y reinicio al comienzo cuando sea apropiado). Tal enfoque mantendría innecesariamente en el alcance algunos objetos 'WeakReference', pero el número estaría limitado en relación con el número que estaba vivo a partir del último GC. – supercat

+0

@supercat Se considera, pero desafortunadamente los finalizadores vienen con costos adicionales de memoria + rendimiento y se ejecutarán desde un hilo de fondo y por lo tanto requieren que bloquees o uses colecciones seguras para hilos ... (más gastos generales) –

0

Para cualquiera que necesite usar un ConditionalWeakTable en .NET 2.0 o 3.5 hay un backport de aquí: https://github.com/theraot/Theraot/wiki/Features

+0

Hola Patrick. Acabo de ver tu publicación. Por lo que he leído, los documentos de ConditionalWeakTable no necesariamente mantienen las referencias aunque haya una fuerte referencia al objeto desde el exterior. ¿Tienes otras informaciones? – msedi

Cuestiones relacionadas