Chapter 5, “Understanding Polymorphism,” as an example. A good object-oriented developer would probably write a save and load function on each CellularAutomataCell class descendant and then call those methods for each cell within the CellularAutomataGame class. Of course, each descendant of the CellularAutomataCell class has different types of properties to store, so you’d probably have to write methods that could be overridden in each descendant class.The .NET Framework has made this tedious programming task much easier, you’ll be happy to know. Built into the .NET Framework is the ability to serialize any object into several different predefined formats and then write that formatted data to (and later read from) disk. Better still, you can do this serializing and reading/writing in a few short lines of code.To demonstrate this functionality, I’ve made a copy of the source code for the CellularAutomata project and added the load/save functionality. You can find this new project in the folder CellularAutomataWithSave.Toimplement the load/save functionality, made the modifications described in the following sections.
Marking Classes As Serializable
Any classes that are going to be sent to disk must first be marked as such. To do this, you add the Serializable attribute to the CellularAutomataCell class and all its descendants. An attribute is a compiler directive that describes some aspect of your class to the .NET Framework. This particular attribute tells the compiler that this class can be serialized to disk. The syntax for the Serializable attribute is as follows:
<Serializable()> _
Public Class ConwaysLifeCell
Inherits CellularAutomataCell
Note the code continuation character—attributes must reside on the same line as the class definition itself.You can attach a number of attributes to both classes and class members, and you can create your own attributes to help describe your code in other ways. For example, you can create a special attribute that denotes the author of a class and the date it was last changed. You can then write special programs that actually rip through your compiled programs, extract this custom attribute information, and output it for documentation purposes. Consult the online help for further information on attributes.
Pointing to the Formatters
The next required step is to add Imports clauses to the top of the code module that will be implementing the serialization. In this case, the code module is CellularAutomata.vb:
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Runtime.Serialization.Formatters.Soap
Usually you’d choose to use only one data format and not need both of these references, both formats will be demonstrated here.
Writing the Save Code
The SaveToDisk method you add to the CellularAutomataGame class is laughably simple:
Public Sub SaveToFile(ByVal cFilename As String)
Dim oFS As New FileStream(cFilename, FileMode.Create)
Dim oBF As New SoapFormatter
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor
Try
oBF.Serialize(oFS, FCells)
Catch oEX As Exception
MsgBox(oEX.Message)
Finally
oFS.Close()
System.Windows.Forms.Cursor.Current = Cursors.Default
End Try
End Sub
All that’s happening here is that a FileStream is opened, a special class named a SoapFormatter is created, and then this class is used to write the data to the stream. The data written in this case is the FCells object, which is an instance of an ArrayList that’s used to store all of the little CellularAutomataCell objects that make up the current game.Notice that you don’t have to specify how to write out the data. All you’ve done is say “Please output the current state of the FCells object to disk.” The SoapFormatter writes out the ArrayList and all of the objects contained within automatically.Now, the Simple Object Access Protocol (SOAP) format is an Extensible Markup Language (XML) format used to send .NET classes back and forth across the Internet, and as an XML format, it tends to make rather large files. As an alternative, you can also use a BinaryFormatter class and your on-disk files will be much smaller. To use this class, simply replace the declaration to the new formatter:
Dim oBF As New BinaryFormatter
Writing the Load Code
The load from disk code is similar to the save to disk code but with the contents of the file loaded into the correct object. In this case, the ArrayList FCells is assigned to the contents of the file:
Public Sub LoadFromFile(ByVal cFilename As String)
Dim oFS As New FileStream(cFilename, FileMode.Open)
Dim oBF As New SoapFormatter
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor
Try
FCells = CType(oBF.Deserialize(oFS), ArrayList)
Catch oEX As Exception
MsgBox(oEX.Message)
Finally
oFS.Close()
System.Windows.Forms.Cursor.Current = Cursors.Default
End Try
End Sub
Adding Buttons and Test
Now that the class can support file loading and saving, all that remains is to add Load and Save buttons to the main form of the program and to call these methods. In the spirit of true laziness, I’ve hard-coded a filename just to test out the functionality:
Private Sub cbSave_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cbSave.Click
oCell.SaveToFile("c:\fred.lif")
End Sub
Private Sub cbLoad_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles cbLoad.Click
oCell.LoadFromFile("c:\fred.lif")
oP.Invalidate()
End Sub
To verify that the save/load works, fire up one of the automata variants, let it run through a few dozen cycles, then hit the Save button. You can take note of two or three patterns in specific spots on the board so that you can identify them later. Then, resume the current pattern and let it change drastically from the save point. Finally, hit the Load button and see the board state magically resume back to its saved state.The ability to serialize complex objects to disk is a great quick way to implement a save/reload feature for your games without having to write line-by-line I/O code.