- Saving the Data
- Reading in the Data
- Saving Different Data
- Choosing a File Type
- Adding Versioning Information
- Conclusion
Saving Different Data
The preceding examples demonstrate saving a single class of data. Your own program might be more complex, however, in that the users might be working with different types of data, as well as different amounts of data. For example, a vector drawing program would let the user create many different vector drawing objects. This isn’t a matter of a single class being saved—at least, it might not seem like it. In fact, however, the same code will work. The reason is that the serialization features in .NET allow for collections to be saved. All you need to do is gather all your data into a collection object, and then serialize the collection.
Following is a complete program that demonstrates this principle:
Option Strict On Imports System.IO Module Module1 <Serializable()> Class MemberInfo Public FirstName As String Public LastName As String Public Birth As DateTime Public Sub New(ByVal FN As String, _ ByVal LN As String, ByVal Bth As DateTime) FirstName = FN LastName = LN Birth = Bth End Sub Public Sub Show() Console.WriteLine(String.Format(" {0} {1}: {2}", _ FirstName, LastName, Birth)) End Sub End Class <Serializable()> Class WebSiteInfo Public Title As String Public Description As String Public Address As Uri Public Sub New(ByVal Tt As String, _ ByVal De As String, ByVal Ad As Uri) Title = Tt Description = De Address = Ad End Sub Public Sub Show() Console.WriteLine(String.Format(" {0} {1}: {2}", _ Title, Description, Address)) End Sub End Class Sub Main() Dim UserData As New ArrayList UserData.Add(New MemberInfo("Sam", "Smith", _ New DateTime(1968, 8, 13))) UserData.Add(New MemberInfo("Julie", "Jones", _ New DateTime(1972, 9, 15))) UserData.Add(New WebSiteInfo("CNN", "News", _ New Uri("http://www.cnn.com"))) UserData.Add(New WebSiteInfo("Yahoo", "Search engine", _ New Uri("http://www.yahoo.com"))) Dim formatter As New _ Runtime.Serialization.Formatters.Binary.BinaryFormatter Dim stream As New FileStream("c:\temp\userdata.dat", _ FileMode.Create, FileAccess.Write, FileShare.None) formatter.Serialize(stream, UserData) stream.Close() ’ Now read the data back in to another list Dim readdata As New ArrayList Dim readformatter As New _ Runtime.Serialization.Formatters.Binary.BinaryFormatter Dim readstream As New FileStream("c:\temp\userdata.dat", _ FileMode.Open, FileAccess.Read, FileShare.Read) readdata = CType(formatter.Deserialize(readstream), ArrayList) stream.Close() ’ Display the data to see if it worked Dim obj As Object For Each obj In readdata If TypeOf (obj) Is MemberInfo Then Console.WriteLine("MemberInfo") CType(obj, MemberInfo).Show() ElseIf TypeOf (obj) Is WebSiteInfo Then Console.WriteLine("WebSiteInfo") CType(obj, WebSiteInfo).Show() End If Next End Sub End Module
This code declares two different types of data, MemberInfo and WebSiteInfo. The program creates an ArrayList and fills the list with a random mixture of MemberInfo and WebSiteInfo objects. Then the code uses the same technique as described earlier to stream out the data, by writing the ArrayList collection to the stream. Notice that in order for this to work, I had to mark the individual classes with the Serializable attribute. (The ArrayList class is already serializable.)
Reading the data in is also just like the previous example. However, once the data is read back in, I needed to jump through some hoops to analyze the data. Since this is a sample program, I know what data I put in; however, in a real program, I might not know exactly what data is in the list I read back in. So I wrote this sample code as if I didn’t know what data to expect, checking the types of the instances. Notice that I’m calling TypeOf and testing the type against my two classes.
However, in this case all I’m doing is calling member functions to write out the data. Both classes have a member function called Show. What I could have done, then, is to derive both classes from a common class that provides an overrideable method called Show. Then I could cast the incoming data to that base class, and just call Show. That would alleviate the need to check the type.
Also, Visual Basic .NET allows for late binding. A lot of people don’t realize that you can do this, but if you turn off the Option Strict (by removing the line altogether or by replacing On with Off), you can simply store the incoming objects in an instance of type Object. Then you just make the call to the Show method without even testing the type:
Dim obj As Object For Each obj In readdata obj.Show() Next
Yes, the compiler allows that. You can put any name you want after the obj, and the compiler will allow it. Only when the program runs will the .NET runtime determine whether the object has the member you’re trying to call. For example, if I called obj.Abcdef(), the compiler will build it just fine. After I run it, I’ll find out that the object doesn’t have an Abcdef member, and I’ll get an exception error:
Public member ’Abcdef’ on type ’MemberInfo’ not found.
In general, such late binding is bad programming style. However, it may be acceptable in your situation.