Using the BinaryFormatter class:
Class Class1
Public Property List1 as New List(Of Something)
Publ
BinaryFormatter doesn't call a class Constuctor, nor it initializes the class object in any way: the opposite, it creates the object non-initialized, in memory.
You can see this in the .Net Source code: when the ParseObject() method is called, the class Object is generated by the GetUninitializedObject(Type type) method.
The next call is made to a [MethodImpl]
function, nativeGetUninitializedObject((RuntimeType)type)
, so we have to stop here, but it's already quite clear what happened: the returned Object
represents a non-initialized type, thus none of the non-serialized fields have been initialized nor the class constructor has been called.
If you don't actually need to use the BinaryFormatter
class, Json.Net serializer/deserializer does initialize a class object instance when deserializing the type. It also respects the <NonSerialized> attribute. If you decide to use it, you don't need to modify the class object definition.
If you must use a BinaryFormatter, you have two options:
internal
(friend
)/protected
) Implementing IDeserializationCallback
:
<NonSerialized>
applies to fields: the List2
Auto property is changed into an Instance Field (the Property version is preserved in the next example):
<Serializable>
Class Class1
Implements IDeserializationCallback
<NonSerialized>
Public List2 As List(Of Integer) = New List(Of Integer)()
Public Property List1 As New List(Of String)
Public Sub OnDeserialization(sender As Object) Implements IDeserializationCallback.OnDeserialization
List2 = New List(Of Integer)
End Sub
End Class
Using the <OnDeserializing>
attribute:
When the <OnDeserializing>
attribute is added to a method, we don't need to implement the IDeserializationCallback
Interface.
Here, a new standard Property with a backing field is added to the Class type.
The <NonSerialized>
attribute is applied to List3
's backing field:
(as a note, the [field: NonSerialized]
attribute has been added to Properties in c# 7.3
)
<Serializable>
Class Class1
<NonSerialized>
Public List2 As List(Of Integer) = Nothing
<NonSerialized>
Private m_List3 As List(Of Double)
Public Sub New()
List2 = New List(Of Integer)
End Sub
Public Property List1 As New List(Of String)
Public Property List3 As List(Of Double)
Get
Return m_List3
End Get
Set
m_List3 = Value
End Set
End Property
<OnDeserializing()>
Friend Sub OnDeserialization(ByVal context As StreamingContext)
List2 = New List(Of Integer)()
m_List3 = New List(Of Double)()
End Sub
End Class
In both cases, the BinaryFormatter's Deserialize()
method will recreate the serialized class object with the non-serialized Lists initialized but empty:
Dim formatter = New BinaryFormatter()
Dim cls1 = New Class1() With {
.List1 = New List(Of String) From {"1", "2", "3"},
.List2 = New List(Of Integer) From {4, 5, 6}
}
Using writer = New FileStream(Path.Combine(AppContext.BaseDirectory(),
"Class1Serialized.bin"), FileMode.Create, FileAccess.Write)
formatter.Serialize(writer, cls1)
End Using
Dim cls1Deserialized As Class1 = Nothing
Using reader = New FileStream(Path.Combine(AppContext.BaseDirectory(),
"Class1Serialized.bin"), FileMode.Open, FileAccess.Read)
cls1Deserialized = TryCast(formatter.Deserialize(reader), Class1)
End Using
Using NewtonSoft.Json
:
Dim cls1 = New Class1() With {
.List1 = New List(Of String) From {"1", "2", "3"},
.List2 = New List(Of Integer) From {4, 5, 6}
}
Dim class1Json = JsonConvert.SerializeObject(cls1)
Dim class1 = JsonConvert.DeserializeObject(Of Class1)(class1Json)