The act of serialization transforms an object (and all of its associated objects and/or data elements) into a stream of bytes, suitable for storage or transmission across a network. The reverse of this act, called deserialization, is to take the same stream of bytes and reconstitute the objects exactly as they were at the time of serialization.
This act, which sounds simple in theory, encompasses a number of points that must be addressed. For starters, the serialization libraries must provide complete reference semanticsthat is, if an object holds two references to other objects, both of which happen to point to the same object, then the serialization mechanism needs to keep that in place. Therefore, when the stream is deserialized, both references point to the same object again.
In addition, the actual format of the stream of bytes may be different from application to application. For example, for storage into a binary column in a database, the serialized representation must be as compact and succinct as possibleno "wasted" bytes. But if we want to send the serialized data over an HTTP link to a non-.NET process, then a binary format is entirely inappropriate, and an XML-based one is more useful.
The System.Runtime.Serialization namespace and its child namespace, System.Runtime.Serialization.Formatters (with its own two child namespaces, System.Runtime.Serialization.Formatters.Binary and System.Runtime.Serialization.Formatters.Soap), directly addresses these needs. System.Runtime.Serialization contains the types necessary to perform the serialization of an object into a stream of bytes, using an alternative object (which implements the IFormatter interface) to actually format the bytes into either binary or XML form. While it is certainly feasible to write your own custom formatters, most .NET programmers have no real practical need to do so, since a binary format and an XML format cover most needs.
Serialization does not necessarily come for free, howeverthere are a few things a .NET programmer must do in order to take advantage of the Serialization mechanism. For starters, a type must be marked as serializable in order to be eligible for serialization; this requires adding the System.SerializableAttribute to the type's declaration. By default, when a type becomes serializable, all nonstatic fields within that type are transformed into bytes when serialized. If a field is itself nonserializable, an exception is thrown; fields that wish to remain unserialized (that is, remain empty during the serialization process) must be marked with the System.NonSerializedAttribute in the type declaration.
It is possible to take greater control over the serialization process by implementing the ISerializable interface and providing definitions for the methods declared there; however, most .NET programmers are generally satisfied with the default serialization behavior.
Figure 38-1 shows the types in this namespace.