Figure 1 Serialization and Deserialization [Serializable]
public class Hashtable : ISerializable, IDeserializationCallback {
// Hashtable's internal fields go here (not shown)
SerializationInfo _savedSI; // Only used for deserialization
// Method to control Hashtable serialization
// I recommend using an Explicit Interface Method Impl. here
void ISerializable.GetObjectData(
SerializationInfo info, StreamingContext context) {
// Call info.AddValue to manually add most fields of the
// Hashtable object to the serialization stream (not shown)
// Create Object arrays for the Hashtable's key/value objects
Object[] keys = new Object[countOfEntries];
Object[] values = new Object[countOfEntries];
// Fill the arrays with references to the Hashtable's
// key/value objects (not shown)
// Add the key/value objects to the serialization stream
info.AddValue("Keys", keys);
info.AddValue("Values", values);
}
// Special constructor (required by ISerializable) to
// control Hashtable deserialization
protected Hashtable(
SerializationInfo info, StreamingContext context) {
// Save the serialization stream info in a member.
// We want to postpone populating the Hashtable object until the
// key/value objects have been completely deserialized since
// the fields in the key objects are likely to affect
// the hash code value for these key objects
_savedSI = info;
}
// Method called after all key/value objects have been deserialized
// I recommend using an Explicit Interface Method Impl. here
void IDeserializationCallback.OnDeserialization(Object sender) {
// All objects in the stream have been deserialized.
// We can now populate the Hashtable object.
// Call _savedSI.GetValue to manually initialize most fields of
// the Hashtable object from the serialization stream (not shown)
// Get the key/value objects from the serialization stream
Object[] keys = _savedSI.GetValue("Keys");
Object[] values = _savedSI.GetValue("Values");
// Add each key/value pair to the Hashtable
for (Int32 x = 0; x < keys.Length; x++) {
Add(keys[x], values[x]);
}
// Set this field to null so that the SerializationInfo object
// can be reclaimed by the garbage collector
_savedSI = null;
}
}
Figure 2 ISerializable using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Reflection;
///////////////////////////////////////////////////////////////////////
[Serializable]
class Person {
private String title;
public Person(String title) {
this.title = title;
}
public override String ToString() {
return String.Format("{0}", title);
}
}
///////////////////////////////////////////////////////////////////////
[Serializable]
class Employee : Person {
private String title;
public Employee(String title) : base("Person") {
this.title = title;
}
public override String ToString() {
return String.Format("{0} -> {1}", title, base.ToString());
}
}
///////////////////////////////////////////////////////////////////////
[Serializable]
class Manager : Employee, ISerializable {
private String title;
public Manager() : base("Employee") {
this.title = "Manager";
}
void ISerializable.GetObjectData(
SerializationInfo info, StreamingContext context) {
// Serialize the desired values for this class
info.AddValue("title", title);
// Get the set of serializable members for our class and base classes
Type thisType = this.GetType();
MemberInfo[] mi =
FormatterServices.GetSerializableMembers(thisType, context);
// Serialize the base class's fields to the info object
for (Int32 i = 0 ; i < mi.Length; i++) {
// Don't serialize fields for this class
if (mi[i].DeclaringType == thisType) continue;
info.AddValue(mi[i].Name, ((FieldInfo) mi[i]).GetValue(this));
}
}
protected Manager(SerializationInfo info, StreamingContext context) :
base(null) {
// Get the set of serializable members for our class and base classes
Type thisType = this.GetType();
MemberInfo[] mi = FormatterServices.GetSerializableMembers(
thisType, context);
// Deserialize the base class's fields from the info object
for (Int32 i = 0 ; i < mi.Length; i++) {
// Don't deserialize fields for this class
if (mi[i].DeclaringType == thisType) continue;
// To ease coding, treat the member as a FieldInfo object
FieldInfo fi = (FieldInfo) mi[i];
// Set the field to the deserialized value
fi.SetValue(this, info.GetValue(fi.Name, fi.FieldType));
}
// Deserialize the values that were serialized for this class
title = info.GetString("title");
}
public override String ToString() {
return String.Format("{0} -> {1}", title, base.ToString());
}
}
///////////////////////////////////////////////////////////////////////
class App {
public static void Main() {
FileStream stream = new FileStream(@"Data.dat", FileMode.Create);
IFormatter formatter = new SoapFormatter();
Manager m = new Manager();
Console.WriteLine(m.ToString());
formatter.Serialize(stream, m);
stream.Close();
stream = new FileStream(@"Data.dat", FileMode.Open);
m = (Manager) formatter.Deserialize(stream);
stream.Close();
Console.WriteLine(m.ToString());
}
}
Figure 3 StreamingContext Properties Member Name | Member Type | Description | State | StreamingContextStates | A set of bit-flags indicating the source or destination of the objects being serialized/deserialized | Context | Object | A reference to an object that contains any user-desired context information | Figure 4 Bit Flag Values Flag Name | Flag Value | Description | CrossProcess | 0x0001 | Source or destination is a different process on the same machine | CrossMachines | 0x0002 | Source or destination is on different machine | File | 0x0004 | Source or destination is a file. Don't assume that same process will deserialize the data | Persistence | 0x0008 | Source or destination is a store such as a database, file, or other. Don't assume that same process will deserialize the data | Remoting | 0x0010 | Source or destination is remoting to an unknown location. The location may be on the same machine but may also be on another machine | Other | 0x0020 | Source or destination is unknown | Clone | 0x0040 | Object graph is being cloned. The serialization code may assume that same process will deserialize the data and it is therefore safe to access handles or other unmanaged resources | CrossAppDomain | 0x0080 | Source or destination is a different AppDomain | All | 0x00FF | Source or destination may be any of the above contexts. This is the default context | Figure 5 Serialize and Deserialize a Singleton // There should be only one instance of this type per AppDomain
[Serializable]
public sealed class Singleton : ISerializable {
// This is the one instance of this type
private static readonly Singleton theOneObject = new Singleton();
// Here are the instance fields
public String someString;
public Int32 someNumber;
// Private constructor allowing this type to construct the singleton
private Singleton() {
// Do whatever is necessary to initialize the singleton
someString = "This is a string field";
someNumber = 123;
}
// Method returning a reference to the singleton
public static Singleton GetSingleton() {
return theOneObject;
}
// Method called when serializing a Singleton
// I recommend using an Explicit Interface Method Impl. here
void ISerializable.GetObjectData(
SerializationInfo info, StreamingContext context) {
info.SetType(typeof(SingletonSerializationHelper));
// No other values need to be added
}
// NOTE: The special constructor is NOT necessary because
// it's never called
}
[Serializable]
internal sealed class SingletonSerializationHelper : IObjectReference {
// This object has no fields (although it could)
// Method called after this object is deserialized
public Object GetRealObject(StreamingContext context) {
return Singleton.GetSingleton();
}
}
Figure 6 Verifying Singleton Code static void SingletonSerializationTest(Stream stream) {
// Create an array with multiple elements refering to
// the one Singleton object
Singleton[] a1 = {
Singleton.GetSingleton(),
Singleton.GetSingleton()
};
Console.WriteLine(
"Do both array elements refer to the same object? " +
(a1[0] == a1[1])); // Displays "True"
// Serialize the array elements
formatter.Serialize(stream, a1);
// Deserialize the array elements
stream.Position = 0;
Singleton[] a2 = (Singleton[]) formatter.Deserialize(stream);
Console.WriteLine(
"Do both array elements refer to the same object? " +
(a2[0] == a2[1])); // Displays "True"
Console.WriteLine(
"Do all array elements refer to the same object? " +
(a1[0] == a2[0])); // Displays "True"
}
|