Using a file stream isn’t mandatory—you can serialize an object’s state into any type of stream, such as a network or memory stream. Example 9-12 demonstrates how to serialize and deserialize an object to and from a memory stream, using the same definitions as in Example 9-4.
Example 9-12. Serialization and deserialization using a memory stream
MyClass obj = new MyClass(); obj.Number1 = 123; IGenericFormatter formatter = new GenericBinaryFormatter(); //Create a memory stream Stream stream = new MemoryStream(); using(stream) { formatter.Serialize(stream,obj); obj = null; stream.Position = 0 ; //Seek to the start of the memory stream obj = formatter.Deserialize<MyClass>(stream); } Debug.Assert(obj.Number1 == 123);
.NET remoting uses a memory stream when marshaling an object by value across app domains. Marshaling by value is covered in Chapter 10.
You can actually use a memory stream to clone a serializable object, too. Example 9-13 shows the static Clone()
method of the SerializationUtil
static helper class.
Example 9-13. Cloning a serializable object
public static class SerializationUtil { static public T Clone<T>(T source) { Debug.Assert(typeof(T).IsSerializable); IGenericFormatter formatter = new GenericBinaryFormatter(); Stream stream = new MemoryStream(); using(stream) { formatter.Serialize(stream,source); stream.Seek(0,SeekOrigin.Begin); T clone = formatter.Deserialize<T>(stream); return clone; } } //Rest of SerializationUtil }
The Clone()
method first verifies that the object passed in for cloning is serializable, by obtaining the type of the source object. The type Type
provides a Boolean read-only property called IsSerializable
, which returns true
if the type has the Serializable
attribute. Clone()
then uses the GenericBinaryFormatter
helper class to serialize and deserialize the object into and out of the memory stream. Clone()
returns the deserialized object (in essence, a deep copy of the source object). Using Clone()
is straightforward:
[Serializable] public class MyClass {...} MyClass obj1 = new MyClass(); MyClass obj2 = SerializationUtil.Clone(obj1);
[Serializable] public class MyClass : ICloneable { public object Clone() { return SerializationUtil.Clone(this); } }
A noteworthy aspect of using streams in serialization is that there are no limits to the number of objects or types you can serialize into a stream. It all depends on the way you manage the stream and the sequence in which you write and read the information. For example, to serialize additional objects into the same stream, all you need to do is continue to write to the stream with the formatter. Of course, you have to deserialize the objects in exactly the same order in which you serialized them, as shown in Example 9-14. You can use the same formatter object or create a new one.
Example 9-14. Serializing multiple objects to the same stream
[Serializable] public class MyClass {...} [Serializable] public class MyOtherClass {...} MyClass obj1 = new MyClass(); MyClass obj2 = new MyClass(); MyOtherClass obj3 = new MyOtherClass(); IGenericFormatter formatter = new GenericBinaryFormatter(); Stream stream; stream = new FileStream(@"C: empobj.bin",FileMode.Create,FileAccess.Write); using(stream) { formatter.Serialize(stream,obj1); formatter.Serialize(stream,obj2); formatter.Serialize(stream,obj3); } obj1 = obj2 = null; obj3 = null; //Later on: stream = new FileStream(@"C: empobj.bin",FileMode.Open,FileAccess.Read); using(stream) { obj1 = formatter.Deserialize<MyClass>(stream); obj2 = formatter.Deserialize<MyClass>(stream); obj3 = formatter.Deserialize<MyOtherClass>(stream); }
You can achieve a similar effect with memory streams, too, as long as the memory stream automatically allocates additional memory.
Another option is to append the state of additional objects to an existing storage. In the case of a file stream, open the file in append mode:
MyClass obj1 = new MyClass();
MyClass obj2 = new MyClass();
IGenericFormatter formatter = new GenericBinaryFormatter();
//Create a new file
Stream stream;
stream = new FileStream(@"C: empobj.bin",FileMode.Create,FileAccess.Write);
using(stream)
{
formatter.Serialize(stream,obj1);
}
//Append another object
stream = new FileStream(@"C: empobj.bin",FileMode.Append,FileAccess.Write);
using(stream)
{
formatter.Serialize(stream,obj2);
}