11.5 [Serializable]

In the preceding examples we assumed for
simplicity's sake that the object graph was
serializable, and we focused on how serialization and deserialization
occurred. Now let's spend some time defining what it
means for an object or object graph to be serializable, and how to
mark your classes and structs as such.

Types that should support being serialized and deserialized by the
runtime must be marked with the [Serializable]
attribute, as follows:

This sets a special serializable bit in the
metadata for the type that instructs the runtime to provide
serialization and deserialization support. The
[Serializable] attribute is not inherited, thus
derived types are not automatically serializable. To provide for the
serialization of derived types, mark each type as
[Serializable].

As we saw earlier in the section "Implicit
Serialization," serializing a type implicitly
serializes all members of the type, recursively. This implies that
all members of a type must themselves be serializable for the type to
successfully serialize. This is true regardless of the accessibility
of the members. For example, if you modify the
Person class as follows, it will no longer be
serializable:

Attempting to serialize an instance of Person now
results in a SerializationException being thrown.

Although the combination of the [Serializable]
attribute and the runtime's built-in support for
serializing arbitrary object graphs is powerful, it
doesn't give you much control over the serialization
and deserialization process. However, there are several other ways
for a type to participate in serialization, as we shall see.