Ручки против.Аддхэндлер
-
09-06-2019 - |
Вопрос
Есть ли преимущество в динамическом подключении/отключении обработчиков событий?
Поможет ли отключение обработчиков вручную гарантировать, что на удаленный объект не останется ссылки?
Решение
Речь не идет об использовании 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 для каждой записи базы данных).Я вручную отключаю обработчики, когда они обрабатывают вещи, с которыми я еще не совсем готов (возможно, потому, что я использую неправильные события?:) )
Отключение события вручную может быть важно для предотвращения утечек памяти:объект, который подключается к событию, инициированному другим объектом, не будет подвергаться сборке мусора до тех пор, пока объект, вызывающий событие, не будет подвергнут сборке мусора.Другими словами, «собиратель событий» имеет сильную ссылку на все подключенные к нему «прослушиватели событий».
Большую часть времени фреймворк позаботится об этом за вас.