On serialization, you might find this post useful. (Please put any
follow-up comments on the MSDN forum - I don't read newsgroups.)
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2067909&SiteID=1&mode=1
I think a review of serialization would help a bit. Let's look at
these types a little more closely. Here's my view of these types:
[DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
[Serializable()] <-------------------------
public class List<T> : IList<T>, System.Collections.IList
[Serializable()] <-------------------------
[System.Runtime.InteropServices.ComVisible(false)]
[DebuggerTypeProxy(typeof(System_CollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
public class LinkedList<T>: ICollection<T>,
System.Collections.ICollection, ISerializable,
IDeserializationCallback {
Remember that serializability of a type, at least for the CLR's
BinaryFormatter & SoapFormatter, is defined by the presence of the
SerializableAttribute on the type. ISerializable is more of an
implementation detail of the type, meaning that the type either has
need of some special versioning constraints around compatibility with
previous versions of its serialized format on disk (or over the
network), or that it may explicitly need some additional code to run
on deserialization to restore internal invariants. Sometimes these
needs can be met by marking fields as optionally serializable, but
ISerializable's more general methods were necessary.
IDeserializationCallback takes this a step further, saying that
deserialization of this type is so special, we must explicitly run
some code after the rest of the object graph has been deserialized.
As an example of where this is necessary, consider a hash table that
must call GetHashCode on all keys within the hash table. Note that we
can't persist out the return value of GetHashCode, since hash
functions are likely to be improved from one version of a library to
another. So we must call GetHashCode on the key sometime during
deserialization, but after the individual key has been deserialized.
If the hash table is deserialized before the keys are deserialized,
then calling GetHashCode may return the wrong value.
IDeserializationCallback allows you a chance of working around simple
ordering problems, by providing a post-deserialization callback.
The only confusing thing about ISerializable and
IDeserializationCallback are that if a base type implements these
interfaces, it suggests that derived types should be serializable (ie,
they should have the SerializableAttribute on them), may need to
implement a deserialization constructor, and if they override these
methods, they may need to call the base type's implementation in the
appropriate places. The most common place where this shows up in our
library is Exception - very few developers remember the
deserialization constructor on their own exceptions (which only shows
up as a problem once you start using multiple appdomains, remoting, or
the CLR's new add-in model). But the slightly fishy serialization
design issue is that the implementation details of the type tell you
whether special serialization semantics are required, and this leaks
through in the public interface in terms of these marker interfaces &
requirements on derived types. Given that serialization of a type
isn't something to be done lightly for any type with non-trivial
internal state, this requirement on subclasses is well within reason.
Back to our collections - clearly, List<T> and LinkedList<T> are
serializable. The original poster's question might then be, how would
these type's serialization needs be different? The issue comes down
to only implementation details. For List<T>, it's basically a T[], a
count, and some other uninteresting state around enumeration.
Assuming that T is serializable (and all subclasses of T that are
stored in the List are also serializable), then List<T> can be
serialized & deserialized safely.
So what about LinkedList<T>? Unlike our hash tables, it doesn't
require hash functions with may vary with the CLR or third party
library version changes. So why do anything special here? The answer
is performance - we don't want to serialize out a list of
LinkedListNodes. We instead write out just the interesting data. Now
let's turn a critical eye to this class. Do we need the
OnDeserialization method? Probably not in this version. Does writing
out just the values instead of the complete object graph break
scenarios when a consumer of the list held references to individual
LinkedListNodes? Yes, this does indeed break the notion of
serializing out an entire object graph then being able to recover all
of it faithfully, potentially on another machine with a different
version of the CLR. But our collection designers apparently felt the
performance of serializing out linked lists efficiently outweighed the
one scenario we've (perhaps accidentally) disabled.
I hope this helps you to understand serialization more clearly.
Brian Grunkemeyer
CLR Base Class Library Team