Вопрос

Этот вопрос уже имеет ответ здесь:

У меня есть 3 вопроса, касающиеся событий:

  1. Должен ли я всегда отписаться от подписки событий, которые были подписаны?
  2. Что произойдет, если я не делаю?
  3. В приведенных ниже примерах, как бы вы отписались от подписанных событий?

У меня есть например этот код:

CTOR: Цель: для обновлений свойств базы данных

this.PropertyChanged += (o, e) =>
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
};

И это: цель: для обвязки графического интерфейса.

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();

foreach (Period period in periods)
{
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
    {
        documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
        documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
        documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
    }
    PeriodListViewModel.Add(periodViewModel);
}
Это было полезно?

Решение

1) Это зависит. Обычно это хорошая идея, но есть типичные случаи, где вам не нужно. В основном, если вы уверены, что объект подписки будет объявлен источником события, вы должны отказаться от подписки, в противном случае это создало бы ненужную ссылку.

Однако, если ваш объект подписывается на свои собственные события, как в следующем:

<Window Loaded="self_Loaded" ...>...</Window>

- Тебе не нужно.

2) Подписка на мероприятие делает дополнительную ссылку на объект подписки. Поэтому, если вы не отмените подписку, ваш объект может быть сохранен в живых в этом этаде, делая эффективную утечку памяти. Отписавшись, вы извлекаете эту ссылку. Обратите внимание, что в случае самостоятельной подписки проблема не возникает.

3) Вы можете поделать это:

this.PropertyChanged += PropertyChangedHandler;
...
this.PropertyChanged -= PropertyChangedHandler;

куда

void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
}

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

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

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

Сейчас во многих случаях это не проблема - например, в форме кнопки, которая поднимает Click Событие, скорее всего, будет жить до тех пор, пока форма, на которой он создан, где обработчик обычно подписан ... Так что нет необходимости отписаться. Это очень типично для GUI.

Точно так же, если вы создаете WebClient исключительно с целью одного асинхронного запроса, подпишитесь на соответствующее событие и начните асинхронный запрос, то WebClient Сам будет иметь право на сбор мусора, когда запрос закончил (при условии, что вы не храните ссылку в другом месте).

По сути, вы всегда должны учитывать отношения между производителем и потребителем. Если производитель будет жить дольше, чем вы хотите, чтобы потребитель, или Он собирается продолжать поднимать событие после того, как вы больше не заинтересованы в этом, то вы должны отписаться.

Вы можете взглянуть на Эта статья на MSDN. Цитировать:

Чтобы предотвратить вызов вашего события, когда событие поднят, просто отписаться от события. Чтобы предотвратить утечки ресурсов, важно отписаться от событий, прежде чем утилизировать объект абонента. До тех пор, пока не отпишитесь от события, делегат многоадресной передачи, который лежит в основе события в публикационном объекте, имеет ссылку на делегат, который инкапсулирует обработчик событий подписчика. До тех пор, пока объект публикации проводится эта ссылка, ваш абонентский объект не будет собранным мусором.

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

Позвольте сказать, что вы форма, и вы подписываетесь на контроль, эти два вместе образуют группу. Однако, если у вас есть центральный класс, который управляет формами, и вы подписались на Closed Событие этой формы, они не образуют группу вместе, и вы должны подписаться, как только форма закрыта.

Подписка на мероприятие делает подписанный экземпляр создать ссылку на подписанную экземпляр. Это предотвращает сборку мусора. Итак, когда у вас есть центральный класс, который управляет экземплярами формы, это сохранит все формы в памяти.

WPF является исключением, поскольку у него есть слабая модель событий, где события подписываются на использование слабых ссылок, и она не будет удерживать форму в памяти. Тем не менее, все еще лучше всего подписаться, когда вы не являетесь частью формы.

1.) Должен ли я всегда рассуждать события, которые были подписаны?
Обычно да. Единственным исключением является когда объект, на котором вы подписаны, больше не ссылаются, и будет сбор мусора в ближайшее время.

2.) Что произойдет, если я не делаю?
Объект, на котором вы подписались, проведут ссылку на делегат, который, в свою очередь, содержит ссылку на его this Указатель, и поэтому вы получите утечку памяти.
Или если обработчик был ламдой, он удержит на любую локальные переменные, которые он не связан, что при этом тоже не будет собираться.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top