问题是当调用者忘记处理我的对象时该怎么办。因此,为简洁起见,我跳过了实施IDisposable
。我的问题是:最后一个代码示例有多危险?可以改进吗?还是我太沉迷于优雅?或者也许已经有另一种模式可以更好地做到这一点?
故事开始于尝试包装一些长时间运行的进程,以便从主线程安全地访问它。
Public Class ThisCantRaiseEvents
Private client As UdpClient
Public Sub New(ByVal client As UdpClient)
Me.client = client
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start(client)
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Private Shared Sub DoWork(ByVal state As Object)
Dim client As UdpClient = DirectCast(state, UdpClient)
Try
While True
client.Receive(Nothing)
End While
Catch ex As Exception
End Try
End Sub
End Class
将事件分派到主线程的天真尝试。效果很好,但永远不会超出范围,因为它自己的线程让它保持活力。(请记住,如果线程对象超出范围,线程不会死亡。)
Public Class ThisWillLeak
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal control As Control)
Me.client = client
Me.mainThread = control
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start()
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
RaiseEvent PacketReceived(Me, e)
End Sub
' The worker thread has a pointer to ThisWillLeak
' and so it will live forever!
Private Sub DoWork(ByVal state As Object)
Try
While True
client.Receive(Nothing)
mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf OnPacketReceived), EventArgs.Empty)
End While
Catch ex As Exception
End Try
End Sub
End Class
' 这是我第一次尝试解决上一个示例中的问题。唉,随着 DoWork 方法变得越来越复杂,它很快变得非常难看。
Public Class ThisIsVeryUgly
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal control As Control)
Me.client = client
Me.mainThread = control
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start(New WeakReference(Me))
End Sub
Public Event PacketReceived As EventHandler
Protected Overrides Sub Finalize()
client.Close()
End Sub
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
RaiseEvent PacketReceived(Me, e)
End Sub
Private Shared Sub DoWork(ByVal state As Object)
Dim weakThis As WeakReference = DirectCast(state, WeakReference)
Dim this As ThisIsVeryUgly = DirectCast(weakThis.Target, ThisIsVeryUgly)
Dim client As UdpClient = this.client
this = Nothing
Try
While True
client.Receive(Nothing)
this = DirectCast(weakThis.Target, ThisIsVeryUgly)
this.mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf this.OnPacketReceived), EventArgs.Empty)
this = Nothing
' Repeat for every other place we need to raise an event! Ugh!
End While
Catch ex As Exception
End Try
End Sub
End Class
这是我最新的发明。它很优雅,但有可能导致问题,因为sender
它引发的事件的参数不是用户所期望的。这会给我带来什么问题吗?
Public Class ThisIsAlmostGood
Private client As UdpClient
Private worker As WorkThread
Public Sub New(ByVal client As UdpClient, ByVal mainThread As Control)
Me.client = client
worker = New WorkThread(client, mainThread)
End Sub
Public Custom Event PacketReceived As EventHandler
AddHandler(ByVal value As EventHandler)
AddHandler worker.PacketReceived
End AddHandler
RemoveHandler(ByVal value As EventHandler)
RemoveHandler worker.PacketReceived
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
End RaiseEvent
End Event
Protected Overrides Sub Finalize()
client.Close()
End Sub
Private Class WorkThread
Private client As UdpClient
Private mainThread As Control
Public Sub New(ByVal client As UdpClient, ByVal mainThread As Control)
Me.client = client
Me.mainThread = mainThread
Dim thread As New Thread(AddressOf DoWork)
thread.IsBackground = True
thread.Start()
End Sub
Public Event PacketReceived As EventHandler
Protected Overridable Sub OnPacketReceived(ByVal e As EventArgs)
' But the user expects a pointer to ThisIsAlmostGood,
' not WorkThread! Should I pass Nothing instead?
RaiseEvent PacketReceived(Me, e)
End Sub
Private Sub DoWork(ByVal state As Object)
Try
While True
client.Receive(Nothing)
mainThread.BeginInvoke(New Action(Of EventArgs) _
(AddressOf OnPacketReceived), EventArgs.Empty)
End While
Catch ex As Exception
End Try
End Sub
End Class
End Class