Domanda

Capisco cosa System.WeakReference fa, ma quello che non riesco a cogliere è un esempio pratico di cosa potrebbe essere utile.La lezione in sé mi sembra, beh, un hack.Mi sembra che ci siano altri mezzi migliori per risolvere un problema in cui viene utilizzato un WeakReference negli esempi che ho visto.Qual è l'esempio canonico di dove devi davvero usare un WeakReference?Non stiamo cercando di ottenere? più lontano lontano da questo tipo di comportamento e dall'uso di questa classe?

È stato utile?

Soluzione

Un esempio utile sono i ragazzi che gestiscono il database orientato agli oggetti DB4O.Lì, WeakReferences vengono utilizzati come una sorta di cache leggera:manterrà i tuoi oggetti in memoria solo finché lo farà la tua applicazione, permettendoti di aggiungere una vera cache.

Un altro utilizzo sarebbe nell'implementazione di gestori di eventi deboli.Attualmente, una delle principali cause di perdite di memoria nelle applicazioni .NET è la dimenticanza di rimuovere i gestori eventi.Per esempio.

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

Vedi il problema?Nello snippet sopra, MyForm verrà mantenuto vivo in memoria per sempre finché MyApplication sarà vivo in memoria.Crea 10 MyForms, chiudili tutti, i tuoi 10 MyForms saranno ancora in memoria, mantenuti in vita dal gestore eventi.

Immettere Riferimento debole.Puoi creare un gestore eventi debole utilizzando WeakReferences in modo che someHandler sia un gestore eventi debole per MyApplication.Foo, risolvendo così le perdite di memoria!

Questa non è solo teoria.Pubblicato Dustin Campbell dal blog DidItWith.NET un'implementazione di gestori di eventi deboli utilizzando System.WeakReference.

Altri suggerimenti

Lo uso per implementare una cache in cui le voci inutilizzate vengono automaticamente raccolte con i rifiuti:

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

    /* ... */
   }

Utilizzo un riferimento debole per il mantenimento dello stato nei mixin.Ricorda, i mixin sono statici, quindi quando usi un oggetto statico per associare lo stato a uno non statico, non sai mai quanto tempo sarà necessario.Quindi, invece di mantenere a Dictionary<myobject, myvalue> Conservo un Dictionary<WeakReference,myvalue> per evitare che il mixin trascini le cose troppo a lungo.

L'unico problema è che ogni volta che eseguo un accesso, controllo anche i riferimenti morti e li rimuovo.Non che facciano del male a nessuno, a meno che non siano migliaia, ovviamente.

Ci sono due ragioni per cui dovresti usarlo WeakReference.

  1. Invece di oggetti globali dichiarati come statici:Gli oggetti globali vengono dichiarati come campi statici e i campi statici non possono essere sottoposti a GC (garbage collection) fino al AppDomain è GC.Quindi rischi di avere eccezioni di memoria insufficiente.Invece, possiamo racchiudere l'oggetto globale in a WeakReference.Anche se il WeakReference stesso è dichiarato statico, l'oggetto a cui punta verrà sottoposto a GC quando la memoria è insufficiente.

    Fondamentalmente, usa wrStaticObject invece di staticObject.

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

    Semplice app per dimostrare che l'oggetto statico viene sottoposto a garbage collection quando AppDomain lo è.

    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 dall'output seguente staticThing è GC alla fine anche dopo ThingsWrapper è - cioèGC quando AppDomain è GC.

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

    Invece possiamo avvolgere Thing in un WeakReference.COME wrStaticThing può essere modificato in GC, avremo bisogno di un metodo caricato pigro che ho tralasciato per brevità.

    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
    }
    

    Nota dall'output riportato di seguito wrStaticThing viene inserito in GC quando viene richiamato il thread GC.

    Thing() wrStaticThing
    ~Thing() wrStaticThing
    ~WeakReferenceThing
    WeakReference is dead.
    
  2. Per oggetti la cui inizializzazione richiede molto tempo:Non vuoi che gli oggetti che richiedono molto tempo per l'inizializzazione vengano sottoposti a GC.Puoi mantenere un riferimento statico per evitarlo (con i contro del punto precedente) o usarlo WeakReference.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top