Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
In the previous section I mentioned how we can derive from a
non-serializable class and implement ISerializable. If we do that we must be
aware that most likely there are reasons why the class is not serializable in
the first place. We have to be very careful how we reconstruct the object.
Extending a class that already implements ISerializable, however, creates a
different scenario that deserves some special attention. It is perfectly
compliant with the inheritance rules of the .NET type system to extend a class
that implements ISerializable without providing a new implementation of
GetObjectData() or the deserialization constructor. However, the serialization
formatters in the .NET Framework will always delegate control over the
serialization process to the object when they detect an ISerializable
implementation, even if the implementation belongs to a base class higher up in
the inheritance hierarchy. Since an implementation of GetObjectData() in a base
class can not know how to serialize any members defined in derived classes it
will obviously not serialize them. We will get some strange results if we miss
to implement GetObjectData() and/or the deserialization constructor.
Unfortunately the compiler can not even warn us if we missed implementing
either one of these methods because the code is syntactically valid.
Also, when we inherit from a class that already implements
ISerializable the base class’ implementation is no longer called automatically.
Instead, our new implementation of GetObjectData() is in control of the
serialization process and with control comes responsibility: Our
GetObjectData() method has the responsibility to serialize the entire class
hierarchy. The easiest way to fulfill this responsibility is to call the base
class’ implementation of GetObjectData(), as seen in the following listing.
21
Listing 12.2 A class
derived from a class implement ISerializable has to implement ISerializable as
well.
[Serializable]
public class BaseClass : ISerializable
{
public BaseClass (){}
public BaseClass
(SerializationInfo info, |
StreamingContext
context) | #1
{ |
// … |
}
|
public void
GetObjectData( SerializationInfo info, |
StreamingContext
context ) |
{ |
// … |
}
|
}
[Serializable]
public class DerivedClass :
BaseClass, ISerializable | #2
{
public DerivedClass
(){}
public DerivedClass
(SerializationInfo info, |
StreamingContext
context) | #3
{
|
base( info,
context );
| | #4
// … |
}
|
public new void
GetObjectData( SerializationInfo info,|
StreamingContext
context ) |
{
|
base.GetObjectData(
info, context ); | | #4
// … |
}
|
}
(annotation)
<#1 Base class implements GetObjectData() and a deserialization constructor.>
(annotation)
<#2 The derived class has to be marked serializable and declare
ISerializable.>
(annotation)
<#3 The derived class has to provide implementations for GetObjectData() and
the deserialization constructor.>
(annotation)
<#4 We delegate serialization and deserialization of the base classes to the
base class implementations.>
If a class requires additional action to complete the
initialization of an instance after all fields are assigned, it can implement
the IDeserializationCallback interface. The interface defines the
OnDeserialization()method, which the formatter calls after all referenced
objects are ready to go.
Note that all [Serializable] classes can choose to implement the
IDeserializationCallback interface. It is not restricted to classes that also
implement ISerializable, for example we can also implement the interface to
initialize any fields marked with the [NonSerialized] attribute after the
formatter populated all other class fields.
Object serialization in the .NET Framework does not provide a
built-in versioning scheme. This can quickly become a problem when our
applications attempt to deserialize an outdated version of a class like in the
following scenario. Imagine we added
more fields to a new version of a class. When we shut down our system to
upgrade to the new version, all application objects serialize their state to
persistant storage. Then we install the new version and boot up the system. All
objects serialized before the switch-over are now deserialized, but no values
are available for the newly added fields. The serialization formatter throws an
exception when it tries to find a value for a new field and our system will not
even boot up. Now what? We cannot let that happen. We have to protect our
classes (and our jobs) by adding and make them resistant to version changes.
The best (and only) way to accomplish robust versioning functionality in the
.NET Framework is to implement the ISerializable interface when we add fields
to new versions and manually control deserialization to handle missing values
in the deserialization constructor.