Wenn komplette Objekte gespeichert werden müssen, dann ist Serialisation wohl eine einfache und schnelle Möglichkeit dies zu bewerkstelligen. Das gilt allerdings nur für Klassen, die über SerializableAttribute verfügen. Ansonsten kann man wichtige Daten in ein serialisierbares Objekt speichern. Wir möchten Ihnen eine andere Möglichkeit zeigen, wie man Klassen speichern kann, die nicht über dieses Attribut verfügen.
Hinweise zu dieser Klasse
Diese Vorgehensweise entspricht nicht der üblichen Implementierung der Serialisierung und widerspricht somit der durchdachten Struktur des Frameworks. Diese Klasse arbeitet rekursiv und ist nicht in der Lage Ringbeziehungen zu erkennen. Dadurch kann es zu einer StackOverflowException kommen. Weiterhin bietet sie keine vollständige Funktionalität, beispielsweise eine komplette Ausnahmebehandlung. Wir möchten hierbei lediglich einige Möglichkeiten des Frameworks bezüglich Reflection und Rekursion aufzeigen um Denkanstöße zu geben. Für einen effizienten Einsatz ist sie allerdings kaum zu verwenden, da Reflection relativ langsam arbeitet.
Wir wollen nun aber sukzessiv vorgehen und machen uns erst einmal Gedanken über die Struktur der Klasse. Als Grundbaustein nehmen wir eine generische Klasse, wodurch wir den Typ des zu verwaltenden Objektes angeben können. Im Konstruktor soll das Objekt übergeben werden, welches auf diese Weise gespeichert werden soll. Unser Klasse soll ähnliche Methoden aufweisen wie die im Framework implementierte Serialization, wodurch wir 2 Methoden implementieren: Serialize und Deserialize.
Nun müssen wir uns überlegen, wie wir die Eigenschaften des zu verwaltenden Objektes speichern. Dafür ist es sinnvoll die Eigenschaften und die zugehörigen Werte in einer Klasse abzubilden, die serialisiert werden kann. Wir verwenden dafür das generische Dictionary. Der Key ist der Name der Eigenschaft und Value ist dabei der Wert der Eigenschaft des zu speichernden Objektes.
Dabei stoßen wir aber auf ein Problem. Was ist, wenn der Wert der Eigenschaft vom Typ einer Klasse ist, die ebenfalls als nicht-serialisierbar gekennzeichnet ist? Für den Fall suchen wir uns eine rekursive Lösung. Wird ein Objekt gefunden, welches nicht serialisierbar ist, dann erzeugen wir eine neue Instanz der generischen Dictionary-Klasse und bilden diese nicht-serialisierbare Klasse damit ab. Dieses Dictionary wird nun als Value für die Eigenschaft gespeichert anstelle des Wertes selbst. Dadurch erhalten wir eine Baumstruktur des zu verwaltenden Objektes mit allen Eigenschaften und zugehörigen Werten.
Umgesetzt könnte die Klasse in etwa so aussehen:
VBC#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | ''' <summary> ''' Serialisiert Instanzen von Klassen, die als nicht-serialisierbar gekennzeichnet sind. ''' </summary> ''' <typeparam name="T">Der zu verwaltende Datentyp.</typeparam> ''' <remarks></remarks> Public Class SerializationObject(Of T) #Region " Deklarationen - Felder " Private pToSerialize As T #End Region #Region " Konstruktoren " ''' <summary> ''' Initialisiert eine neue Instanz der generischen SerializationObject-Klasse. ''' </summary> ''' <param name="toSerialize"></param> ''' <remarks></remarks> Public Sub New(ByVal toSerialize As T) pToSerialize = toSerialize End Sub #End Region #Region " Eigenschaften " ''' <summary> ''' Ruft das zu verwaltende Objekt ab. ''' </summary> ''' <value></value> ''' <returns>Das zu verwaltende Objekt.</returns> ''' <remarks></remarks> Public ReadOnly Property Value() As T Get Return pToSerialize End Get End Property #End Region #Region " Methoden - Function " ''' <summary> ''' Gibt an, ob das Objekt als serialisierbar gekennzeichnet ist. ''' </summary> ''' <param name="obj">Das Objekt, welches geprüft werden soll.</param> ''' <returns>true, wenn das Objekt als serialisierbar gekennzeichnet ist, ansonsten false. ''' </returns> ''' <remarks></remarks> Private Function HasSerializableAttribute(ByVal obj As Object) As Boolean ' Iteriere durch alle Attribute des Objektes For Each attr As Object In obj.GetType().GetCustomAttributes(False) ' Prüfe, ob SerializableAttribute; true -> true zurückgeben If attr.GetType Is GetType(SerializableAttribute) Then Return True Next ' false zurückgeben Return False End Function ''' <summary> ''' Bildet das angegebene Objekt mit den Eigenschaften und den zugehörigen Werten als ''' serialisierbares Dictionary ab und gibt dieses zurück. ''' </summary> ''' <param name="obj">Das Objekt, welches als serialisierbares Dictionary abgebildet werden ''' soll.</param> ''' <returns>Das Dictionary, welches die Eigenschaften und zugehörigen Werte des angegebenen ''' Objektes abbildet.</returns> ''' <remarks></remarks> Private Function ReadProperties(ByVal obj As Object) As Dictionary(Of String, Object) Dim dicToSave As New Dictionary(Of String, Object) Dim pi As System.Reflection.PropertyInfo ' Iteriere durch jede Property des Objektes For Each pi In obj.GetType().GetProperties(Reflection.BindingFlags.Instance Or _ Reflection.BindingFlags.Public) ' Prüfe, ob Eigenschaft über einen Get- und Set-Accessor verfügt If pi.CanRead Then ' true -> Wert lesen Dim val As Object = pi.GetValue(obj, New Object() {}) ' Prüfe, ob der Wert serialisierbar ist If HasSerializableAttribute(val) Then ' true -> Name und Wert hinzufügen dicToSave.Add(pi.Name, val) Else ' false -> Name und Wert als Dictionary hinzufügen dicToSave.Add(pi.Name, ReadProperties(val)) End If End If Next ' Dictionary zurückgeben Return dicToSave End Function #End Region #Region " Methoden - Sub " ''' <summary> ''' Deserialisiert die gespeicherten Daten aus einer Datei in das verwaltete Objekt. ''' </summary> ''' <param name="path">Der Pfad der Datei mit den Daten.</param> ''' <remarks></remarks> Public Sub Deserialize(ByVal path As String) Try ' Das zu ladende Dictionary Dim dictToLoad As Dictionary(Of String, Object) ' Verwende den Datenstream Using fs As New System.IO.FileStream(path, IO.FileMode.OpenOrCreate) ' Formatter erzeugen Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter ' Daten deserialisieren dictToLoad = DirectCast(bf.Deserialize(fs), Dictionary(Of String, Object)) ' Werte der Eigenschaften setzen Call SetProperties(pToSerialize, dictToLoad) End Using Catch ex As Exception Debug.WriteLine(ex.Message) End Try End Sub ''' <summary> ''' Serialisiert das verwaltete Objekt in die angegebene Datei. ''' </summary> ''' <param name="path">Der Pfad der Datei, in welche die Daten serialisiert werden sollen.</param> ''' <remarks></remarks> Public Sub Serialize(ByVal path As String) Try ' Das zu speichernde Dictionary Dim dicToSave As Dictionary(Of String, Object) = ReadProperties(pToSerialize) ' Verwende den Datenstream Using fs As New System.IO.FileStream(path, IO.FileMode.OpenOrCreate) ' Formatter erzeugen Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter ' Daten serialisieren bf.Serialize(fs, dicToSave) End Using Catch ex As Exception Debug.WriteLine(ex.Message) End Try End Sub ''' <summary> ''' Setzt die im properties-Parameter angegebenen Eigenschaften mit zugehörigen Werten für das im ''' obj-Parameter angegebene Objekt. ''' </summary> ''' <param name="obj">Das Objekt, dessen Eigenschaften gesetzt werden sollen.</param> ''' <param name="properties">Das Dictionary mit den Eugenschaften und zugehörigen Werten.</param> ''' <remarks></remarks> Private Sub SetProperties(ByVal obj As Object, ByVal properties As Dictionary(Of String, Object)) ' Variablen deklarieren Dim pi As System.Reflection.PropertyInfo = Nothing Dim propValue As Object = Nothing ' Iteriere durch jeden Eintrag im Dictionary For Each kvp As KeyValuePair(Of String, Object) In properties ' Entsprechende Eigenschaft des Objektes finden pi = obj.GetType().GetProperty(kvp.Key, Reflection.BindingFlags.Instance Or _ Reflection.BindingFlags.Public) ' Prüfe, ob die Eigenschaft gefunden wurde If pi IsNot Nothing Then ' true -> Prüfe, ob es sich um ein Dictionary handelt und der Datentyp der Eigenschaft ' kein Dictionary ist (das Dictionary bildet also eine Klasse ab) If TypeOf kvp.Value Is Dictionary(Of String, Object) AndAlso _ pi.PropertyType IsNot GetType(Dictionary(Of String, Object)) Then ' true -> Instanz erstellen und als zu schreibenden Wert speichern propValue = Activator.CreateInstance(pi.PropertyType) ' Methode rekursiv aufrufen um die Eigenschaften der erzeugten Instanz zu setzen Call SetProperties(propValue, DirectCast(kvp.Value, Dictionary(Of String, Object))) Else ' false -> Wert lesen und als zu schreibenden Wert speichern propValue = kvp.Value End If ' Prüfe, ob die Eigenschaft geschrieben werden kann If pi.CanWrite Then ' true -> Wert speichern pi.SetValue(obj, propValue, New Object() {}) End If End If Next End Sub #End Region End Class |
1 | Kein Code vorhanden |
