tags:

views:

30

answers:

2

Hi All,

Long term lurker, first time poster here.

I have written a class to model an object in vb.net, using vs2008 and framework 2.0. I am serializing the class to an XML file for persistent storage. I do this with a method in the class like this:

Public Sub SaveAs(ByVal filename As String)
    Dim writer As New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
    Dim file As New System.IO.StreamWriter(filename)
    writer.Serialize(file, Me)
    file.Close()
End Sub

I now want to do a similar thing but reading the class from file to the current instance, like this:

Public Sub New(ByVal filename As String)
    Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
    Dim file = New System.IO.StreamReader(FullPath)
    Me = CType(reader.Deserialize(file), MyNamespace.MyClass)
End Sub

However, I cannot assign anything to “Me”. I’ve tried creating a temporary object to hold the file contents then copying each property and field over to the current instance. I iterated over the properties (using Reflection), but this soon gets messy, dealing with ReadOnly collection properties, for example. If I just copy each property manually I will have to remember to modify the procedure whenever I add a property in the future, so that sounds like a recipe for disaster.

I know that I could just use a separate function outside the class but many built-in .NET classes can instantiate themselves from file e.g. Dim bmp As New Bitmap(filename As String) and this seems very intuitive to me.

So can anyone suggest how to load a class into the current instance in the Sub New procedure? Many thanks in advance for any advice.

A: 

I'd put a shared load function on the class, that returned the newly de-serialised object. e.g.

Public Class MyClass
...
Public shared Function Load(ByVal filename As String) as MyClass
    Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass)) 
    Dim file = New System.IO.StreamReader(FullPath) 
    Return CType(reader.Deserialize(file), MyNamespace.MyClass) 
End Sub 
End Class
...
Dim mine as MyClass = MyClass.Load("MyObject.Xml");

Hope this helps

Alternatively,
Encapsulate the data of your class in an inner, private class.
The properties on your outer visible class delegate to the inner class. Then Serialising and De-serialising happens on the inner class, you can then have a ctor that takes the file name, de-serialises the inner hidden object, and assigns it to the classes data store.

Binary Worrier
Thanks Binary Worrier, I used the shared load function as it was a simple solution that my tiny mind could cope with. Thanks again.
Guy
Guy, Joel gave good advice. Load function shouldn't take file name, should take XmlReader or other stream object. Allows greater composition of objects, and allows you to test the function without reading/writing files. You should give him +1 too :)
Binary Worrier
OK, will do. Not sure how the whole plus minus thing works, to be honest.
Guy
Hmm...don't seem to be able to +1 without creating an account and logging in. Logging in will detach me from this thread. Hmm...
Guy
Got signed in but can't vote because my reputation is in the gutter somewhere. Sorry.
Guy
@Guy: No worries. Hang out for a while, ask / answer a couple of questions, you'll be surprised how quickly the rep adds up :)
Binary Worrier
+1  A: 

The "New" method in VB.Net is a constructor for the class. You can't call it for an existing instance, as the whole purpose of the method is to create new instances; it's just not how the language works. Try naming the method something like "ReadFrom" or "LoadFrom" instead.

Additionally, given those methods, I would try to implement them using a Factory Pattern. The ReadFrom method would be marked Shared and return the new instance. I would also make the method more generic. My main ReadFrom() method would accept an open textreader or xmlreader or even just a stream, rather than a file name. I would then have overloads that converts a file name into a stream for reading and calls the main method.

Of course, that assumes I use that pattern in the first place. .Net already has great support for xml serialization built into the platform. Look into the System.Xml.Serialization.XmlSerializer class and associated features.

Joel Coehoorn
Thanks for your answer, Joel. I don't want to call Sub New() on an existing instance, I want to load the instance with data from the XML file during instantiation.
Guy