【问题标题】:Subscribing event handlers in a factory在工厂中订阅事件处理程序
【发布时间】:2018-08-27 21:55:31
【问题描述】:

我正在编写一个需要通过 TCP 套接字接收消息的 VB.Net 应用程序,但我的问题确实与套接字无关。为简单起见,假设我想创建一个类来处理来自 Socket 类事件的数据,该类事件是我们公司创建的类库的一部分。在研究依赖注入时,this articlethis one 表示不要将 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

【问题讨论】:

  • 您能提供一个minimal reproducible example吗?我可以在 VB.NET 控制台应用程序中复制、粘贴和运行的东西吗?
  • 我已将代码更新为可以粘贴到控制台应用程序并运行的代码。在实际应用程序中,有更复杂的机制来创建和销毁连接。关键点是 TODO,真正应该删除事件处理程序的地方。因为它是 Socket 对象将永远存在。

标签: .net dependency-injection factory


【解决方案1】:

这对你有用吗?

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

【讨论】:

  • 在真实系统中,MessageConnection 对象是基于与其他硬件的连接来创建或销毁的,因此它比单个函数持续时间更长。我只是以这种方式编写它以降低示例的复杂性。但我也许可以使用异步函数而不是操作来完成这项工作,该操作返回一个在连接完成时完成的任务。让我玩这个然后回复你。
  • @RobL - 你可以让一个动作持续多久,只要你喜欢。它最终有助于实施更具功能性的编码风格。
  • 当然,但如果我不让它异步它会阻止调用者。
  • @RobL - 这是您第一次提到您不想阻止来电者。并且使用异步函数并不妨碍您使用操作。事实上,你可以将两者结合使用。
  • 这很好用。我确实通过实验发现大多数时候我不需要这种复杂程度。事件订阅保存在套接字中,仅由发送方引用,仅由 MessageConnection 对象引用。所以要摆脱它们,我只需要停止引用 MessageConnection 实例,GC 将(最终)将所有内容删除到套接字,因此不会泄漏。但是我确实有一种情况是保留了套接字,我需要即时添加和删除订阅者。在这种情况下,上面的例子效果很好。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 2021-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多