11.7 IDeserializationCallback
It is helpful to think of deserialization as a special form of
constructor call, since the end result is a valid object reference.
While the [NonSerialized] attribute is useful for
controlling which data is stored in the serialized form of the object
or sent over the wire on a remoting call, the problem is that the
fields marked with [NonSerialized] are not
assigned meaningful values when the constructor (i.e., the
deserialization process) finishes executing. This can result in class
invariants being violated, such as the implicit in the preceding
sample, in which Age is intended to store the
Person's age based on the
DateOfBirth.
One possible solution to this problem is the
IDeserializationCallback interface. Implementing
this interface on your type indicates to the runtime that you wish to
participate in the deserialization process. This interface contains a
single method, OnDeserialization, which the
runtime invokes after it has finished constructing your object, but
before it returns the fully constructed instance to the client. This
method gives you a chance to perform whatever fixups you need to
inside the object, and is a way to ensure that class invariants are
preserved before clients can access the instance.
Using our sample Person class, the
IDeserializationCallback interface implementation
might look like this:
[Serializable]
public sealed class Person : IDeserializationCallback {
public string Name;
public DateTime DateOfBirth;
[NonSerialized] public int Age; // Can be calculated
public void OnDeserialization(object o) {
TimeSpan ts = DateTime.Now - DateOfBirth;
Age = ts.Days/365; // Rough age in years
}
// Rest of class...
}
By combining the [Serializable] attribute with the
[NonSerialized] attribute and the
IDeserializationCallback interface, it is possible
to add serialization support to complex object graphs with relatively
little code.
|