Практическое использование System.WeakReference

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
36
0
6 501
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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

    /* ... */
   }
Ответ принят как подходящий

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

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

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

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

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

Это не просто теория. Дастин Кэмпбелл из блога DidItWith.NET разместил реализация слабых обработчиков событий, используя System.WeakReference.

Да, это довольно здорово. Здесь мы приняли его код с некоторыми дополнениями для обработки других типов обработчиков событий (например, неуниверсальный обработчик событий, PropertyChangedEventHandler и т. д.). У нас это сработало очень хорошо.

Judah Gabriel Himango 25.03.2009 17:19

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

Pissu Pusa 29.01.2020 04:18

Вот пример: github.com/adbancroft/WeakEvents.Fody/blob/master/…

Judah Gabriel Himango 30.01.2020 05:02

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

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

Начиная с .NET 4.0, ConditionalWeakTable, вероятно, более подходит для этого использования.

user2864740 02.11.2020 00:14

В частности, CWT автоматически закрывает последнее примечание «[не забудьте] проверить мертвые ссылки и удалить их», как Dictionaryпозволит ли нет восстанавливать значения до тех пор, пока записи не будут фактически удалены.

user2864740 02.11.2020 00:20

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

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

    Как правило, используйте 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 при вызове потока GC.

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

Другие вопросы по теме