views:

230

answers:

3

I'm developing a viewer that will be able to open all of the custom documents that we produce via our software. All of the documents inherit from IDocument, but I'm not sure how to go about deserializing (in a good way - nested try/catch could probably work, but that would be hideous).

So my method as it is now looks like this:

public Boolean OpenDocument(String filename, Type docType, out IDocument document)
{
    // exception handling etc. removed for brevity

    FileStream fs = null;
    BinaryFormatter bFormatter = new BinaryFormatter();

    fs = new FileStream(filename, FileMode.Open);
    document = (docType)bFormatter.Deserialize(fs);

    return true;
}

Obviously this doesn't work as I can't use the variable docType that way, but I think it illustrates the point of what I'm trying to do. What would be the proper way to go about that?

edit> @John ok, maybe I should append another question: if I have an interface:

public interface IDocument
{
    public Int32 MyInt { get; }
}

and a class:

public class SomeDocType : IDocument
{
    protected Int32 myInt = 0;
    public Int32 MyInt { get { return myint; } }

    public Int32 DerivedOnlyInt;
}

if I deserialize to an IDocument, will the DerivedOnlyInt be a part of the object - such that after deserializing, I can cast to SomeDocType and it'll be fine?

+4  A: 

Why not just cast to IDocument? What benefit do you believe casting to the exact type would have here?

If you want the caller to get the result in a strongly typed way, here's how I'd write it, using generics:

public T OpenDocument<T>(String filename) where T : IDocument
{
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();    
        return (T) bFormatter.Deserialize(fs);
    }
}

Of course, that relies on the caller knowing the right type at compile-time. If they don't, there's no way they're going to know how to use the right type anyway, so just return IDocument:

public IDocument OpenDocument(String filename)
{
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        BinaryFormatter bFormatter = new BinaryFormatter();    
        return (IDocument) bFormatter.Deserialize(fs);
    }
}

EDIT: To answer the edit to your question, yes the derived property would still exist. Casting doesn't actually change an object - it just means you get a reference which the compiler knows is of an appropriate type.

Jon Skeet
I use a generic approach to wrap my serialization functions. This is the best way IMHO
JoshBerke
Very nice. The caller in this case does know the document type (via the file extension from the openfiledialog). Thank you Jon
SnOrfus
@Jon this doesn't seem to work for XmlSerialization. I was able to get a question and answer that works but feels a bit kludgy, are we missing something: http://stackoverflow.com/questions/1145791/xml-object-deserialization-to-interface
ahsteele
+1  A: 

You are using the BinaryFormatter class which includes type information into the serialized stream, so you don't need the docType variable:

public Boolean OpenDocument(String filename, out IDocument document)
{
    // exception handling etc. removed for brevity

    FileStream fs = null;
    BinaryFormatter bFormatter = new BinaryFormatter();

    fs = new FileStream(filename, FileMode.Open);
    document = (IDocument)bFormatter.Deserialize(fs);

    return true;
}
Darin Dimitrov
+1  A: 

If you can easily figure out what kind of document it is (ie: it's part of the file header), it might be easiest to have a strategy for deserializing that is specific to each type.