Pregunta

entiendo lo que Sistema.Referencia débil lo hace, pero lo que parece que no puedo entender es un ejemplo práctico de para qué podría ser útil.La clase en sí me parece, bueno, un truco.Me parece que existen otras formas mejores de resolver un problema en el que se utiliza WeakReference en los ejemplos que he visto.¿Cuál es el ejemplo canónico de dónde realmente debes usar una WeakReference?¿No estamos tratando de conseguir más lejos lejos de este tipo de comportamiento y uso de esta clase?

¿Fue útil?

Solución

Un ejemplo útil son los chicos que ejecutan la base de datos orientada a objetos DB4O.Allí, las WeakReferences se utilizan como una especie de caché ligero:mantendrá sus objetos en la memoria sólo mientras lo haga su aplicación, permitiéndole colocar un caché real encima.

Otro uso sería la implementación de controladores de eventos débiles.Actualmente, una gran fuente de pérdidas de memoria en las aplicaciones .NET es olvidarse de eliminar los controladores de eventos.P.ej.

public MyForm()
{
    MyApplication.Foo += someHandler;
}

¿Ves el problema?En el fragmento anterior, MyForm se mantendrá vivo en la memoria para siempre mientras MyApplication esté vivo en la memoria.Cree 10 MyForms, ciérrelos todos, sus 10 MyForms aún estarán en la memoria y el controlador de eventos los mantendrá vivos.

Introduzca Referencia Débil.Puede crear un controlador de eventos débil utilizando WeakReferences para que someHandler sea un controlador de eventos débil para MyApplication.Foo, solucionando así sus pérdidas de memoria.

Esto no es sólo teoría.Dustin Campbell del blog DidItWith.NET publicado una implementación de controladores de eventos débiles utilizando System.WeakReference.

Otros consejos

Lo uso para implementar un caché donde las entradas no utilizadas se recolectan automáticamente como basura:

class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();

   public TValue this[TKey key]
    { get {lock(dict){ return getInternal(key);}}
      set {lock(dict){ setInteral(key,value);}}     
    }

   void setInteral(TKey key, TValue val)
    { if (dict.ContainsKey(key)) dict[key].Target = val;
      else dict.Add(key,new WeakReference(val));
    } 


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

   /// <summary>Removes any dead weak references</summary>
   /// <returns>The number of cleaned-up weak references</returns>
   public int CleanUp()
    { List<TKey> toRemove = new List<TKey>(dict.Count);
      foreach(KeyValuePair<TKey,WeakReference> kv in dict)
       { if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
       }

      foreach (TKey k in toRemove) dict.Remove(k);
      return toRemove.Count;
    }

    public bool Contains(string key) 
     { lock (dict) { return containsInternal(key); }
     }

     bool containsInternal(TKey key)
      { return (dict.ContainsKey(key) && dict[key].IsAlive);
      }

     public bool Exists(Predicate<TValue> match) 
      { if (match==null) throw new ArgumentNullException("match");

        lock (dict)
         { foreach (WeakReference weakref in dict.Values) 
            { if (   weakref.IsAlive 
                  && match((TValue) weakref.Target)) return true;
         }  
      }

       return false;
     }

    /* ... */
   }

Utilizo referencias débiles para mantener el estado en mixins.Recuerde, los mixins son estáticos, por lo que cuando usa un objeto estático para adjuntar un estado a uno no estático, nunca se sabe cuánto tiempo será necesario.Entonces, en lugar de mantener un Dictionary<myobject, myvalue> mantengo un Dictionary<WeakReference,myvalue> para evitar que el mixin arrastre las cosas por mucho tiempo.

El único problema es que cada vez que hago un acceso, también reviso si hay referencias muertas y las elimino.No es que hagan daño a nadie, a menos que sean miles, claro.

Hay dos razones por las que usarías WeakReference.

  1. En lugar de objetos globales declarados como estáticos:Los objetos globales se declaran como campos estáticos y los campos estáticos no pueden ser GC'ed (recolección de basura) hasta que AppDomain está GC.Por lo tanto, corre el riesgo de sufrir excepciones por falta de memoria.En cambio, podemos envolver el objeto global en un WeakReference.A pesar de WeakReference en sí mismo se declara estático, el objeto al que apunta será GC cuando la memoria sea baja.

    Básicamente, utiliza wrStaticObject en lugar de staticObject.

    class ThingsWrapper {
        //private static object staticObject = new object();
        private static WeakReference wrStaticObject 
            = new WeakReference(new object());
    }
    

    Aplicación simple para demostrar que el objeto estático se recolecta como basura cuando AppDomain lo es.

    class StaticGarbageTest
    {
        public static void Main1()
        {
            var s = new ThingsWrapper();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
    class ThingsWrapper
    {
        private static Thing staticThing = new Thing("staticThing");
        private Thing privateThing = new Thing("privateThing");
        ~ThingsWrapper()
        { Console.WriteLine("~ThingsWrapper"); }
    }
    class Thing
    {
        protected string name;
        public Thing(string name) {
            this.name = name;
            Console.WriteLine("Thing() " + name);
        }
        public override string ToString() { return name; }
        ~Thing() { Console.WriteLine("~Thing() " + name); }
    }
    

    Nota del resultado siguiente staticThing es GC al final incluso después ThingsWrapper es - es decirGC cuando AppDomain está GC.

    Thing() staticThing
    Thing() privateThing
    ~Thing() privateThing
    ~ThingsWrapper
    ~Thing() staticThing
    

    En lugar de eso podemos envolver Thing en un WeakReference.Como wrStaticThing se puede editar con GC, necesitaremos un método de carga diferida que omití por brevedad.

    class WeakReferenceTest
    {
        public static void Main1()
        {
            var s = new WeakReferenceThing();
            s = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (WeakReferenceThing.wrStaticThing.IsAlive)
                Console.WriteLine("WeakReference: {0}", 
                    (Thing)WeakReferenceThing.wrStaticThing.Target);
            else 
                Console.WriteLine("WeakReference is dead.");
        }
    }
    class WeakReferenceThing
    {
        public static WeakReference wrStaticThing;
        static WeakReferenceThing()
        { wrStaticThing = new WeakReference(new Thing("wrStaticThing")); }
        ~WeakReferenceThing()
        { Console.WriteLine("~WeakReferenceThing"); }
        //lazy-loaded method to new Thing
    }
    

    Tenga en cuenta de la salida a continuación que wrStaticThing se procesa por GC cuando se invoca el hilo de GC.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Para objetos que requieren mucho tiempo para inicializarse:No desea que los objetos que requieren mucho tiempo para iniciarse sean GC.Puede mantener una referencia estática para evitarlo (con las desventajas del punto anterior) o usar WeakReference.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top