Я пишу приложение VB.Net, которое должно получать сообщения через TCP-сокеты, но мой вопрос действительно не имеет ничего общего с сокетами. Для простоты, скажем, я хочу создать класс, который обрабатывает данные, поступающие из события класса Socket, который является частью библиотеки классов, созданной нашей компанией. При исследовании внедрения зависимостей в этой статье и в этой говорится, что не нужно вставлять экземпляр Socket в конструктор процессора сообщений только для того, чтобы процессор мог подписаться на событие. Вместо этого я должен дать своему процессору публичную функцию и позволить корню композиции создать сокет и процессор, и соединить обработчик с сокетом, чтобы он вызывал публичную функцию на процессоре. Сначала это прекрасно работало.
Но теперь у меня есть случай, когда сокеты нужно создавать и утилизировать динамически. Проблема с вышеупомянутой техникой заключается в том, что сокет, процессор и подписка на события будут жить в течение всего срока службы приложения. Я думал, что смогу использовать фабрику для создания объекта процессора. Затем фабрика создаст сокет, процессор и подпишется на обработчик событий. Но он только возвращает процессор клиенту, так как он вообще может отписаться от обработчика? Я не хочу вызывать утечки из-за невозможности отписаться от обработчика событий.
Обновлено
Следующий код является полным консольным приложением. Надеюсь, я нашел правильный баланс между простотой и объяснением проблемы.
Imports System.Collections.Concurrent
Module Module1
Sub Main()
'Create a connection
Dim ConnectionClient As New ConnectionClient(New MessageConnectionFactory)
'Do some sending and receiving
ConnectionClient.DoSomething()
'Wait so the user can see results
Console.WriteLine("Press any key")
Console.ReadLine()
End Sub
End Module
Public Class DataProducedEventArgs
Inherits EventArgs
Public Property Data As Byte()
End Class
Public Class Socket
Public Event DataProduced As EventHandler(Of DataProducedEventArgs)
'In a real application, the event would fire when we receive data on the socket.
'This provides a way to simulate that.
Public Sub SimulateReceivedData(Data As Byte())
RaiseEvent DataProduced(Me, New DataProducedEventArgs With {.Data = Data})
End Sub
Public Sub Send(Data As String)
Console.WriteLine("Sending over socket: {0}", Data)
End Sub
End Class
Public Class MessageProcessor
Public Sub Consume(Data As Byte())
Console.WriteLine("Received {0} bytes of data", Data.Length)
End Sub
End Class
Public Class MessageSender
Private _socket As Socket
Public Sub New(socket As Socket)
_socket = socket
End Sub
Public Sub Send(Message As String)
_socket.Send(Message)
End Sub
End Class
Public Class MessageConnection
Public Property Processor As MessageProcessor
Public Property Sender As MessageSender
End Class
Public Class ConnectionClient
Private _Factory As MessageConnectionFactory
Private Connections As New ConcurrentQueue(Of MessageConnection)
Public Sub New(factory As MessageConnectionFactory)
_Factory = factory
End Sub
Public Sub DoSomething()
'Get a connection
Dim Connection As MessageConnection = _Factory.Create
'Do something with it
Connection.Sender.Send("Test")
'TODO - I could dispose of it here but that doesn't
'remove the event handlers
End Sub
End Class
Public Class MessageConnectionFactory
Public Function Create() As MessageConnection
'Create the objects
Dim MySocket As New Socket
Dim Connection As New MessageConnection
Connection.Processor = New MessageProcessor
Connection.Sender = New MessageSender(MySocket)
'How could this handler ever get removed?
AddHandler MySocket.DataProduced, Sub(sender, e) Connection.Processor.Consume(e.Data)
'The client only cares about the message connection object, not the socket
Return Connection
End Function
End Class