Respond to events from a list of class instances
I have a .NET class, InstrumentConnector, that has an event, StatusChanged. I want to create several instances of this class (each class handles a separate file) and respond when the StatusChanged event is raised any instance. How would I do this in .NET? Do I want to create a List(Of InstrumentConnector) or a similar IEnumerable? How do I write the event handler to respond to a particular instance's StatusChanged event?
Here is the InstrumentConnector class and the module with the StatusChanged handler:
Public Class InstrumentConnector
Private _inProcessDir As String
Private _doneDir As String
Private _dataFileName As String
Public ReadOnly Property DataFileName() As String
Get
Return _dataFileName
End Get
End Property
Private WithEvents _processTimer As Timers.Timer
Private _fileStatus As Status
Public ReadOnly Property FileStatus() As Status
Get
Return _fileStatus
End Get
End Property
Public Sub New(ByVal inProcessDirectory As String, ByVal doneDirectory As String)
_inProcessDir = inProcessDirectory
_doneDir = doneDirectory
_fileStatus = Status.Waiting
End Sub
Public Sub New(ByVal inProcessDirectory As String)
Me.New(inProcessDirectory, IO.Path.Combine(inProcessDirectory, "done"))
End Sub
Private Sub InitializeTimer()
_processTimer = New Timers.Timer
With _processTimer
.Interval = 1000
End With
End Sub
Public Sub Process(ByVal dataFileName As String)
_dataFileName = IO.Path.GetFileName(dataFileName)
InitializeTimer()
_processTimer.Start()
End Sub
Private Sub _processTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles _processTimer.Elapsed
'1. Look for file in Done directory
'2. Evaluate its approval status
'3. If not completed change approval setting in file and move to InProcess directory
If IsFileInProcessDirectory() = True Then
MoveFileToDoneDirectory()
Else
If IsFileInDoneDirectory() = True Then
If IsFileApproved() = True Then
_fileStatus = Status.Accepted
RaiseEvent StatusChanged(Nothing)
Else
If NeedsSecondAttempt() = True Then
MoveFileToProcessDirectory()
Else
_fileStatus = Status.Rejected
RaiseEvent StatusChanged(Nothing)
End If
End If
End If
End If
End Sub
Private Sub MoveFileToDoneDirectory()
Dim fileContents As String
fileContents = My.Computer.FileSystem.ReadAllText(IO.Path.Combine(_inProcessDir, _dataFileName))
If GetFileApprovalStatus() = True Then
fileContents = String.Concat("9999", fileContents.Substring(4))
End If
My.Computer.FileSystem.WriteAllText(IO.Path.Combine(_doneDir, _dataFileName), fileContents, False)
My.Computer.FileSystem.DeleteFile(IO.Path.Combine(_inProcessDir, _dataFileName))
End Sub
Private Function NeedsSecondAttempt() As Boolean
Dim fileContents As String
fileContents = My.Computer.FileSystem.ReadAllText(IO.Path.Combine(_doneDir, _dataFileName))
If fileContents.StartsWith("9999") And fileContents.Substring(111, 1) = "1" Then
Return False
Else
Return True
End If
End Function
Private Sub MoveFileToProcessDirectory()
Dim fileContents As String
fileContents = My.Computer.FileSystem.ReadAllText(IO.Path.Combine(_doneDir, _dataFileName))
fileContents = fileContents.Remove(111, 1).Insert(111, "1")
My.Computer.FileSystem.WriteAllText(IO.Path.Combine(_doneDir, _dataFileName), fileContents, False)
My.Computer.FileSystem.MoveFile(IO.Path.Combine(_doneDir, _dataFileName), IO.Path.Combine(_inProcessDir, _dataFileName))
End Sub
Private Function IsFileInDoneDirectory() As Boolean
Dim fileExists As Boolean
fileExists = My.Computer.FileSystem.FileExists(IO.Path.Combine(_doneDir, _dataFileName))
Return fileExists
End Function
Private Function IsFileInProcessDirectory() As Boolean
Dim fileExists As Boolean
fileExists = My.Computer.FileSystem.FileExists(IO.Path.Combine(_inProcessDir, _dataFileName))
Return fileExists
End Function
Private Function IsFileApproved() As Boolean
Dim fileContents As String
fileContents = My.Computer.FileSystem.ReadAllText(IO.Path.Combine(_doneDir, _dataFileName))
If fileContents.Substring(0, 4) = "9999" Then
Return True
Else
Return False
End If
End Function
Private Function GetFileApprovalStatus() As Boolean
Dim randNum As New Random
Dim nextNum = randNum.Next(1, 10)
'60% of all samples will be approved
If nextNum > 6 Then
Return False
Else
Return True
End If
End Function
Public Event StatusChanged(ByVal e As System.EventArgs)
Protected Overridable Sub OnStatusChanged(ByVal e As System.EventArgs) Handles Me.StatusChanged
_processTimer.Stop()
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
_processTimer.Stop()
End Sub
Public Enum Status
Accepted
Rejected
Waiting
End Enum
End Class
Module Module1
Private WithEvents detector As NewFileDetector
Private WithEvents newIC As InstrumentConnector
Private ICList As New List(Of InstrumentConnector)
Private fileDirectory As String
Sub Main()
fileDirectory = "c:\temp\inproc"
Console.WriteLine("Mock IC program running...")
Console.WriteLine()
Console.WriteLine("Press ESC to quit...")
detector = New NewFileDetector(fileDirectory)
Dim keyresponse As ConsoleKeyInfo = Nothing
Do
keyresponse = Console.ReadKey
Loop Until keyresponse.Key = ConsoleKey.Escape 'Quit program on ESC
End Sub
Private Sub detector_FilesFound(ByVal e As System.EventArgs) Handles detector.FilesFound
Console.WriteLine("Files found: {0}", detector.FileList.Count)
For Each filename In detector.FileList
newIC = New InstrumentConnector(fileDirectory)
ICList.Add(newIC)
newIC.Process(filename)
Next
End Sub
Private Sub newIC_StatusChanged(ByVal e As System.开发者_JAVA百科EventArgs) Handles newIC.StatusChanged
Console.WriteLine("File: {0} Status: {1}", newIC.DataFileName, newIC.FileStatus.ToString)
ICList.Remove(newIC)
End Sub
End Module
A bit late on this, but: this is exactly the problem Microsoft's CAB framework solves. It allows any event to have multiple subscribers and multiple publishers.
There is an open-source version of this framework available here.
How you store the InstrumentConnectors is not related t the events. A List(Of T)
is fine.
And you can hook up a listener class to multiple sources of events. That is why the standard pattern in .NET has the sender
parameter. Just cast that to an InstrumentConnector and you're done.
That is, as long as StatusChanged is designed according to the rules.
OK, Take 2.
There is way too much code now. Hard to get through.
Public Event StatusChanged(ByVal e As System.EventArgs)
Is not defined properly. It should have been
Public Event StatusChanged(ByVal sender as Object, ByVal e As System.EventArgs)
And, my VB is a bit rusty, next to the "Handles " notation you should be able to put a statement (somewhere)
HandlesEvent Me.StatusChangedHandler, someInstrument.Statuschanged ' very sloppy approx
And then in a class of your choice (the Form maybe)
Sub StatusChangedHandler(ByVal sender as Object, ByVal e As System.EventArgs)
Dim InsConn as InstrumentConnector = sender ' may need a CType() here
....
End Sub
精彩评论