Подтвердить что ты не робот

Должен ли я отказаться от подписки на события?

У меня есть 3 вопроса относительно событий:

  • Должен ли я всегда отменить подписку на события, которые были подписаны?
  • Что произойдет, если я НЕ?
  • В приведенных ниже примерах, как вы отмените подписку на подписанные события?

Я имею, например, этот код:

Ctor: Назначение: для обновлений свойств базы данных

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

и это: Цель: для GUI-привязки оберните модель в viewmodels

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

Ответ 1

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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