Подписка на обработчики событий на фабрике - PullRequest
0 голосов
/ 28 августа 2018

Я пишу приложение 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

1 Ответ

0 голосов
/ 28 августа 2018

Может ли это работать на вас?

Public Class ConnectionClient

    Private _Factory As MessageConnectionFactory

    Public Sub New(factory As MessageConnectionFactory)
        _Factory = factory
    End Sub

    Public Sub DoSomething()
        _Factory.Using(Sub(c) c.Sender.Send("Test"))
    End Sub

End Class

Public Class MessageConnectionFactory
    Public Sub [Using](action As Action(Of MessageConnection))
        Dim MySocket As New Socket
        Dim Connection As New MessageConnection
        Connection.Processor = New MessageProcessor
        Connection.Sender = New MessageSender(MySocket)

        Dim handler = New EventHandler(Of DataProducedEventArgs)(Sub(sender, e) Connection.Processor.Consume(e.Data))
        AddHandler MySocket.DataProduced, handler
        action(Connection)
        RemoveHandler MySocket.DataProduced, handler
    End Sub
End Class
...