.NET 2.0 introduces support for serialization events.
.NET calls designated methods on your class when serialization and
deserialization take place. Four serialization and deserialization
events are defined. The serializing event is raised just before serialization takes place, and the serialized event is raised just after serialization. Similarly, the deserializing event is raised just before deserialization, and the deserialized event
is raised after deserialization. Both classes and structures can take
advantage of serialization events. You designate methods as
serialization event handlers using method attributes, as shown in Example 1.
Example 1. Applying the serialization event attributes
[Serializable]
public class MyClass
{
[OnSerializing]
void OnSerializing(StreamingContext context)
{...}
[OnSerialized]
void OnSerialized(StreamingContext context)
{...}
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{...}
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{...}
}
|
Note that the class itself must still be marked
for serialization. Each serialization event-handling method must have
the following signature:
void <Method Name>(StreamingContext context);
This is required because internally .NET still
uses delegates to subscribe and invoke the event-handling methods. If
the attributes are applied on methods with incompatible signatures, .NET will throw a SerializationException.
StreamingContext is a structure informing the object why it is being serialized. The StreamingContext structure provides the State property of the enum type StreamingContextStates. Possible reasons for serialization include remoting (across app domain or process), persisting to a file, and so on. The context
parameter is largely ignored; it is used only in advanced esoteric
scenarios in which the serialization and deserialization actions are
context-sensitive.
The event attributes are defined in the System.Runtime.Serialization namespace.
As the attribute names imply, the OnSerializing attribute designates a method handling the serializing event, and the OnSerialized attribute designates a method handling the serialized event. Similarly, the OnDeserializing attribute designates a method handling the deserializing event, and the OnDeserialized attribute designates a method handling the deserialized event. Figure 1 is a UML activity diagram depicting the order in which events are raised during serialization.
.NET first raises the serializing event, thus
invoking the corresponding event handlers (there can be more than one,
as you will see shortly). Next, .NET serializes the object, and finally
the serialized event is raised and its event handlers are invoked.
Figure 2 is a UML activity diagram depicting the order in which deserialization events are raised.
Unlike with the serialization events, with deserialization .NET has to accommodate the use of IDeserializationCallback. .NET first raises the deserializing event and then performs the deserialization. If the class implements IDeserializationCallback, .NET then calls the OnDeserialization( )
method and finally raises the deserialized event. Note that in order to
call the deserializing event-handling methods .NET has to first
construct an object—however, it does so without ever calling your
class's default constructor.
The next version of the serialization
infrastructure (a part of Indigo) will provide a compatible programming
model that supports an analogous set of serialization events. |
|
1. Applying the Event Attributes
.NET allows you to apply the same serialization event attributes on multiple methods of the class. For example:
[OnSerializing]
void OnSerializing1(StreamingContext context)
{...}
[OnSerializing]
void OnSerializing2(StreamingContext context)
{...}
The canonical example for applying the same
serialization event attribute on multiple methods is when dealing with
partial classes:
[Serializable]
public partial class MyClass
{
[OnSerializing]
void OnSerializing1(StreamingContext context)
{...}
}
public partial class MyClass
{
[OnSerializing]
void OnSerializing2(StreamingContext context)
{...}
}
This decoupled the various parts of the partial
class, because it enables each part of the class to deal with the class
members it is responsible for, and have a dedicated serialization event
handling method for them.
While you can also apply multiple attributes on the same event-handling method, as follows:
[OnSerializing]
[OnSerialized]
void OnSerialization(StreamingContext context)
{...}
the usefulness of doing so is
questionable. The method will be called once per attribute, and there is
no easy way to detect which event is raised inside the method.