Вопрос

Есть ли преимущество в динамическом подключении/отключении обработчиков событий?

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

Это было полезно?

Решение

Речь не идет об использовании AddHandler вместо Handles.

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

У меня были ситуации в приложениях Windows Forms (дни .NET 1.1), когда обработчик событий вызывался для элементов управления, у которых не было других ссылок на них (и которые по сути были мертвы, и я бы подумал, что они были собраны с помощью GC) -- чрезвычайно сложно отладить.

Я бы использовал RemoveHandler, чтобы избавиться от обработчиков элементов управления, которые вы не собираетесь использовать повторно.

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

Я почти уверен, что Handles Предложение является просто синтаксическим сахаром и вставляет AddHandler оператор в ваш конструктор.Я тестировал этот код и отключил фреймворк приложения, чтобы в конструкторе не было лишних вещей:


Public Class Form1

    Public Sub New()
        ' This call is required by the Windows Form Designer. '
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call. '
        AddHandler Me.Load, AddressOf Form1_Load
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim breakpoint As Integer = 4
    End Sub
End Class

В итоге ИЛ получился таким:

  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  ldarg.0
  IL_000a:  dup
  IL_000b:  ldvirtftn  instance void WindowsApplication1.Form1::Form1_Load(object,
                                                                           class [mscorlib]System.EventArgs)
  IL_0011:  newobj     instance void [mscorlib]System.EventHandler::.ctor(object,
                                                                          native int)
  IL_0016:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)

 '... lots of lines here '

  IL_0047:  ldarg.0
  IL_0048:  callvirt   instance void WindowsApplication1.Form1::InitializeComponent()
  IL_004d:  nop
  IL_004e:  ldarg.0
  IL_004f:  ldarg.0
  IL_0050:  dup
  IL_0051:  ldvirtftn  instance void WindowsApplication1.Form1::Form1_Load(object,
                                                                           class [mscorlib]System.EventArgs)
  IL_0057:  newobj     instance void [mscorlib]System.EventHandler::.ctor(object,
                                                                          native int)
  IL_005c:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Form::add_Load(class [mscorlib]System.EventHandler)
  IL_0061:  nop
  IL_0062:  nop
  IL_0063:  ret
} // end of method Form1::.ctor

Обратите внимание на два идентичных блока кода вокруг IL_000b и IL_0051.Я думаю, это просто синтаксический сахар.

Объявление поля как WithEvents заставит компилятор автоматически сгенерировать свойство с таким именем.Геттер возвращает значение резервного поля.Сеттер немного сложнее.Сначала он проверяет, имеет ли резервное поле правильное значение.Если да, то он выходит.В противном случае, если резервное поле не равно нулю, он отправляет запросы «RemoveHandler» для всех своих событий объекту, идентифицированному резервным полем.Далее, независимо от того, было ли резервное поле ненулевым, оно устанавливает его равным запрошенному значению.Наконец, если новое значение не равно NULL, независимо от того, было ли старое значение или нет, свойство отправляет запросы «AddHandler» на все свои события объекту, идентифицируемому новым значением.

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

Я считаю, что динамическое подключение/отключение обработчиков событий полезно только в том случае, если у вас есть долгоживущий объект, предоставляющий события, которые используются многими недолговечными объектами.В большинстве других случаев два объекта удаляются примерно в одно и то же время, и CLR самостоятельно выполняет достаточную работу по очистке.

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

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

Большую часть времени фреймворк позаботится об этом за вас.

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