Ручки против AddHandler

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

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

7 ответов

Решение

Это не вопрос использования AddHandler против Handles.

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

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

Я бы использовал 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 закончился так:

  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" обо всех своих событиях к объекту, указанному в поле поддержки. Далее, независимо от того, было ли вспомогательное поле ненулевым, оно устанавливает его равным запрошенному значению. Наконец, если новое значение не равно нулю, независимо от того, было ли старое или нет, свойство выдает запросы AddHandler для всех своих событий для объекта, идентифицируемого новым значением.

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

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

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

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

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

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