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