venerdì, settembre 07, 2007

Serializzare una classe con eventi

Dovento sfruttare i meccanismi di serializzazione interni a vb.net si può incappare in un problema.
Quando la classe da serializzare possiede eventi gestiti da oggetti non serializzabili (es una window form) la serializzazione si blocca.
Tutto nasce dall'impossibilità di dichiarare non serializzabili gli eventi.

Seguendo le indicazioni da http://www.lhotka.net/WeBlog/CommentView.aspx?guid=776f44e8-aaec-4845-b649-e0d840e6de2c si giunge ad una soluzione :

Utilizzando la dichiarazione estesa degli eventi si può utilizzare un doppio elenco degli oggetti che gestiscono gli eventi generati della classe, gli oggetti serializzabili e quelli non serializzabili.

_
Private mNonSerializableHandlers As New Generic.List(Of EventHandler)
Private mSerializableHandlers As New Generic.List(Of EventHandler)

Public Custom Event NameChanged As EventHandler

AddHandler(ByVal value As EventHandler)
If value.Target.GetType.IsSerializable Then
mSerializableHandlers.Add(value)
Else
If mNonSerializableHandlers Is Nothing Then
mNonSerializableHandlers = _
New Generic.List(Of EventHandler)()
End If
mNonSerializableHandlers.Add(value)
End If
End AddHandler

RemoveHandler(ByVal value As EventHandler)
If value.Target.GetType.IsSerializable Then
mSerializableHandlers.Remove(value)

Else
mNonSerializableHandlers.Remove(value)
End If
End RemoveHandler

RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
For Each item As EventHandler In mNonSerializableHandlers
item.Invoke(sender, e)
Next
For Each item As EventHandler In mSerializableHandlers
item.Invoke(sender, e)
Next
End RaiseEvent
End Event


In questo modo il problema è aggirato, inutile dire che la sintassi necessaria è decisamente molto estesa, sforzo ripagato dalla serializzazione.
Nel caso in cui ci siano eventi con parametri, è necessario appesantire ulteriormente, utilizzando un delegato:

_
Private mNonSerializableHandlers As New Generic.List(Of On_Progress)
Private mSerializableHandlers As New Generic.List(Of On_Progress)
Public Delegate Sub On_Progress(ByVal status As String, ByVal progress As Integer)
Public Custom Event Progress As On_Progress
AddHandler(ByVal value As On_Progress)
If value.Target.GetType.IsSerializable Then
mSerializableHandlers.Add(value)
Else
If mNonSerializableHandlers Is Nothing Then
mNonSerializableHandlers = New Generic.List(Of On_Progress)()
End If
mNonSerializableHandlers.Add(value)
End If

End AddHandler

RemoveHandler(ByVal value As On_Progress)
If value.Target.GetType.IsSerializable Then
mSerializableHandlers.Remove(value)
Else
mNonSerializableHandlers.Remove(value)
End If
End RemoveHandler
'
RaiseEvent(ByVal status As String, ByVal progress As Integer)
For Each item As On_Progress In mNonSerializableHandlers
item.Invoke(status, progress)
Next
For Each item As On_Progress In mSerializableHandlers
item.Invoke(status, progress)
Next
End RaiseEvent
End Event


In questo modo è possibile serializzare e deserializzare la nostra classe

'SERIALIZE
Dim FS As New System.IO.FileStream("c:\prova.wbp", FileMode.OpenOrCreate)
Dim BinFormatter As New BinaryFormatter
'
BinFormatter.Serialize(FS, myClassWithEvents)



'DESERIALIZE
Dim FS As New System.IO.FileStream(file, FileMode.Open)
Dim BinFormatter As New BinaryFormatter
'
RETURN BinFormatter.Deserialize(FS)


#