While storing information in memory is great, there comes a time your
users will have to shut your application down. This means (probably)
that you will need to write information to a file at some point, because
you will want to store whatever data was in memory. Today, we are going
to take a look at a feature built into .NET called Serialization that
makes writing and reading data structures to and from a file extremely
easy.
For this example, let's say I want to create a program that keeps track
of all the cars my friends own. I'm going to create two objects to
achieve this:
Car
and Owner
. The Car
object will store the make,
model, and year of the car. The Owner
object will save some
information about who owns the car. Each Car
object will hold a
reference to an Owner
object.//information about the car
public class Car
{
private string make;
private string model;
private int year;
private Owner owner;
public Car()
{
}
}
//information about the car's owner
public class Owner
{
private string firstName;
private string lastName;
public Owner()
{
}
}
Since most of us have more than one friend, we're going to need to
create a
List
of Car
objects.List<Car> cars = new List<Car>();
Now that we have our objects created, we're almost ready to serialize
them. When I save data to files, I like to create an object specifically
to hold all the things I want to serialize.
public class ObjectToSerialize
{
private List<Car> cars;
public List<Car> Cars
{
get { return this.cars; }
set { this.cars = value; }
}
public ObjectToSerialize()
{
}
}
This class holds a reference to every object we'll want to serialize. In
this case, the only thing we want to save is the list of cars. Now lets
create the functions that will perform the serialization and
deserialization of our object. I usually create a
Serializer
class to
control the writing and reading to and from files.using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class Serializer
{
public Serializer()
{
}
public void SerializeObject(string filename, ObjectToSerialize objectToSerialize)
{
Stream stream = File.Open(filename, FileMode.Create);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, objectToSerialize);
stream.Close();
}
public ObjectToSerialize DeSerializeObject(string filename)
{
ObjectToSerialize objectToSerialize;
Stream stream = File.Open(filename, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
objectToSerialize = (ObjectToSerialize)bFormatter.Deserialize(stream);
stream.Close();
return objectToSerialize;
}
}
As you can see, the actual code required to serialize an object is
relatively small and simple. At this point, however, the code will not
build. Before the
Serialize
function can be called on
ObjectToSerialize
we must include the Serializable attribute and it
must implement the ISerializable
interface.using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable()]
public class ObjectToSerialize : ISerializable
{
private List<Car> cars;
public List<Car> Cars
{
get { return this.cars; }
set { this.cars = value; }
}
public ObjectToSerialize()
{
}
public ObjectToSerialize(SerializationInfo info, StreamingContext ctxt)
{
this.cars = (List<Car>)info.GetValue("Cars", typeof(List<Car>));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("Cars", this.cars);
}
}
As part of the
ISerializable
interface, the class must include another
constructor for deserializing the object and a function GetObjectData
which describes how to serialize the object. Since the Car
and Owner
objects are also being serialized, they will also need to implement
these functions.[Serializable()]
public class Car : ISerializable
{
private string make;
private string model;
private int year;
private Owner owner;
public Car()
{
}
public Car(SerializationInfo info, StreamingContext ctxt)
{
this.make = (string)info.GetValue("Make", typeof(string));
this.model = (string)info.GetValue("Model",typeof(string));
this.year = (string)info.GetValue("Year", typeof(int));
this.owner = (Owner)info.GetValue("Owner", typeof(Owner));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("Make", this.make);
info.AddValue("Model", this.model);
info.AddValue("Make", this.year);
info.AddValue("Owner", this.owner);
}
}
[Serializable()]
public class Owner : ISerializable
{
private string firstName;
private string lastName;
public Owner()
{
}
public Owner(SerializationInfo info, StreamingContext ctxt)
{
this.firstName = (string)info.GetValue("FirstName", typeof(string));
this.lastName = (string)info.GetValue("LastName", typeof(string));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("FirstName", this.firstName);
info.AddValue("LastName", this.lastName);
}
}
Now, to save the list of objects to a file, all that needs to be done is
to call the
Serialize
and DeSerialize
functions of the Serializer
class.List<Car> cars = new List<Car>();
//save the car list to a file
ObjectToSerialize objectToSerialize = new ObjectToSerialize();
objectToSerialize.Cars = cars;
Serializer serializer = new Serializer()
serializer.SerializeObject("outputFile.txt", objectToSerialize);
//the car list has been saved to outputFile.txt
//read the file back from outputFile.txt
objectToSerialize = serializer.DeSerializeObject("outputFile.txt");
cars = objectToSerialize.Cars;
That is all that's required to save and load custom C# objects to a
binary file. Just like any file, it is possible for your files to become
corrupted. Because of this, it is probably a good idea to add some error
handling whenever output from the file is being cast to an object.
Comments
Post a Comment