2008-08-19 3 views
33

Я понимаю, что делает System.WeakReference, но то, что я не могу понять, является практическим примером того, чем он может быть полезен. Сам класс, похоже, мне нравится. Мне кажется, что есть другие, более эффективные способы решения проблемы, где WeakReference используется в примерах, которые я видел. Каков канонический пример того, где вы действительно должны использовать WeakReference? Разве мы не пытаемся получить дальше в стороне от этого типа поведения и использования этого класса?Практическое использование System.WeakReference

ответ

44

Один полезный пример ребята, которые управляют db4o объектно-ориентированную базу данных. Там WeakReferences используются как своего рода световой кеш: он будет хранить ваши объекты в памяти только до тех пор, пока ваше приложение будет работать, что позволит вам поставить настоящий кеш сверху.

Другое применение было бы в реализации слабых обработчиков событий. В настоящее время один большой источник утечек памяти в .NET-приложениях забывает удалить обработчики событий. Например.

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

Уточнить возникшую проблему? В приведенном выше фрагменте MyForm будет храниться в памяти навсегда, пока MyApplication жив в памяти. Создайте 10 MyForms, закройте их все, ваши 10 MyForms по-прежнему будут в памяти, сохранены в памяти обработчиком событий.

Введите WeakReference. Вы можете создать слабый обработчик событий с помощью WeakReferences, чтобы someHandler был слабым обработчиком событий MyApplication.Foo, тем самым устраняя утечки памяти!

Это не просто теория. Дастин Кэмпбелл из блога DidItWith.NET опубликовал an implementation of weak event handlers с использованием System.WeakReference.

+3

+1 для наконечника с обработчиком событий, thats awesome !!!! – 2009-03-24 21:49:22

12

Я использую его для реализации кэша, где неиспользуемые записи автоматически мусора:

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

    /* ... */ 
    } 
2

Я использую слабую ссылку для сохранения состояния в mixins. Помните, mixins являются статическими, поэтому, когда вы используете статический объект для присоединения состояния к нестационарному, вы никогда не знаете, сколько времени потребуется. Поэтому вместо хранения Dictionary<myobject, myvalue> я сохраняю Dictionary<WeakReference,myvalue>, чтобы предотвратить смешение микшинга слишком долго.

Единственная проблема в том, что каждый раз, когда я делаю доступ, я также проверяю мертвые ссылки и удаляю их. Не то, чтобы они причиняли кому-либо боль, если, конечно, не было тысяч.

0

Есть две причины, по которым вы бы использовали WeakReference.

  1. Вместо глобальных объектов, объявленных как статические: глобальные объекты объявлены как статические поля и статические поля не могут быть GC'ed (сборки мусора) до AppDomain является GC'ed. Таким образом, вы рискуете исключениями вне памяти. Вместо этого мы можем обернуть глобальный объект в WeakReference. Несмотря на то, что сам WeakReference объявлен статическим, объект, на который он указывает, будет GC'ed, когда память будет низкой.

    В принципе, используйте wrStaticObject вместо staticObject.

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

    Простое приложение, чтобы доказать, что статический объект является сборкой мусора, когда AppDomain.

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

    Примечание с выхода ниже staticThing является GC'ed в самом конце, даже после того, как ThingsWrapper есть - то есть, когда GC'ed AppDomain является GC'ed.

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

    Вместо этого мы можем обернуть Thing в WeakReference. Поскольку wrStaticThing может быть GC'ed, нам понадобится ленивый метод, который я забыл для краткости.

    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 
    } 
    

    Примечание от выхода ниже этого wrStaticThing является GC'ed, когда АЯ нить вызываются.

    Thing() wrStaticThing 
    ~Thing() wrStaticThing 
    ~WeakReferenceThing 
    WeakReference is dead. 
    
  2. Для объектов, которые требуют много времени для инициализации: Вы не хотите объектов, которые по времени consusming Инициализировать быть GC'ed. Вы можете либо сохранить статическую ссылку, чтобы избежать этого (с минусами из выше точки), либо использовать WeakReference.