Мой вопрос вместо того, чтобы использовать «ссылку на действие», почему «ссылка на MethodInfo» делает трюк? Я предполагаю, что методinfo также должен быть собран.
Нет, это на самом деле не MethodInfo
против Action
, что делает трюк. Это что-то еще, но я должен признать, что эта статья не очень хорошо написана и может ввести в заблуждение. Позвольте мне попытаться объяснить.
События и GC
Скажем, у вас есть класс с событием называется Click
. Класс: Button
Допустим, у вас есть еще один класс, который подписывается на событие от Button
, как это:
public class Subscriber1
{
public Subscriber1(Button button)
{
button.Click += ClickHander;
}
private void ClickHander(object sender, EventArgs e)
{
// ...
}
public void UnSubscribe()
{
button.Click -= ClickHandler;
}
}
Теперь, как класс кнопки знаю, кто уведомить при нажатии на кнопку? Ну, он держит ссылку на все те классы, которые подписались. Таким образом, в вышеприведенном случае будет содержаться ссылка на экземпляр класса Subscriber1
. Всякий раз, когда нажимается кнопка, он будет уведомлять экземпляр Subscriber1
. Так что же произойдет, если мы делаем это:
subcriber1Instance = null;
Что будет происходить в том, что если экземпляр button
еще жив, GC не будет собирать subscriber1Instance
. Зачем? Потому что он коренится: экземпляр button
содержит ссылку на него. Вот почему мы должны делать это вместо:
subscriber1Instance.UnSubscribe();
subcriber1Instance = null;
В действительности Subscriber1
должны осуществлять IDisposable и делать отписки там, но смысл этого ответа не так. Поэтому я сохраняю это просто, поэтому мы не теряем фокус.
Когда мы отменили подписку на событие Click
экземпляра button
, то subscriberIntance
больше не укоренен и его можно очистить GC.
Так что же делать?
В этой статье авторы пытаются решить эту проблему разработчиков, забывая отказаться от подписки и, таким образом, вызывая проблемы с утечкой памяти. Обратите внимание, что это один из способов: только если издатель переживает абонента, он будет поддерживать подписчика живым, а не наоборот.Поэтому, если издатель готов к GC, абонент не сможет сохранить его в живых.
В основном то, что говорят авторы, заключается в том, что абонент не должен подписываться на событие напрямую, поэтому он не приводит к тому, что button
держит твердую ссылку на него. Вместо этого абонент должен получить от WeakReference
и передать его на номер button
при подписке. Как это:
private class WeakSubscriber1 : WeakReference
{
public WeakSubscriber1(Subscriber1 target) : base(target) { }
public void ClickHander(object sender, EventArgs args)
{
Subscriber1 b = (Subscriber1)this.Target;
if (b == null)
{
Button c = sender as Button;
if (c != null)
{
c.MyEvent -= new EventHandler(this.ClickHandler);
}
}
else
{
b.Handler1(sender, args);
}
}
}
Для использования выше будет выглядеть так:
Subscriber1 sub1 = new Subscriber1();
WeakSubscriber1 weak = new WeakSubscriber1(sub1);
Button button = new Button();
button.Click += weak.ClickHandler();
Теперь button
холдинговая слабую ссылку. Это означает, что когда sub1
сделано нулевым, а button
запускает событие, то weak
проверит, sub1
- null. Если да, тогда он проверяет, жив ли еще button
. Если оба значения истинны, то они будут отписаны. Теперь sub1
больше не подписывается на button.Click
, поэтому GC может его собрать.
@Rob да, я согласен с вашим последним предложением. Однако то, что я сбиваю с толку, так это то, что автор подразумевал, что Action будет GCed, если вы сохраните ссылку на него в WeakReference (правильно ли я?), Поэтому зачем хранить ссылку на MethodInfo? –