Search code examples
c#iostreamsaveformatter

Understanding saving - do I only use Stream, or use both Formatter and Stream?


From what I understand, the Formatter converts a serializable object into a stream of bytes and the Stream (e.g. FileStream) does the actual writing of those bytes into a file. Example code:

public static void SaveData(MySerializableData data)
{
    BinaryFormatter formatter = new BinaryFormatter();
    FileStream stream = new FileStream(SavePath, FileMode.Create);        
    formatter.Serialize(stream, data);
    stream.Close();
}

However at other times, I'm also seeing this type of code:

void Save()
{
    string data = "Value 1";
    StreamWriter writer = new StreamWriter("MySaveFile.txt", true);
    writer.Write(data);
}

Why is it that in the second case, we abandon the 2-step process of saving? Why do we sometimes use only StreamWriter, but at other times use both the formatter and a stream object?

Thank you!


Solution

  • BinaryFormatter serializes a class into a byte array and writes it to a stream. It's useful when you want to save/load classes with it's data. Serializtion stores metadata about the class graph that is storing.

    StreamWriter is a stream that has specific functions to write a string into a file.

    Consider this example:

    MemoryStream mstr = new MemoryStream();
    
    string datastr = "hello!";
    
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(mstr, datastr);
    mstr.Seek(0, SeekOrigin.Begin);
    
    string resultString = Encoding.ASCII.GetString(mstr.ToArray());
    

    If you inspect resultString you will find that it contains something like this:

    "\0\u0001\0\0\0????\u0001\0\0\0\0\0\0\0\u0006\u0001\0\0\0\u0006hello!\v"
    

    Well, that's not what you would want to have in a text file, right? As you see serialization is not intended to store raw data but class instances.

    Now check this:

    MemoryStream mstr = new MemoryStream();
    StreamWriter sw = new StreamWriter(mstr);
    string datastr = "hello!";
    sw.Write(datastr);
    mstr.Seek(0, SeekOrigin.Begin);
    sw.Close();
    
    string resultString = Encoding.ASCII.GetString(mstr.ToArray());
    

    If you now inspect resultString it will contain:

    "hello!"
    

    As you see it's very different, that's what you would expect in a text file.

    You can also store raw binary data with a stream:

    byte[] data = new byte[]{ 1,2,3,4 };
    var fs = File.Create("out.dat"); //this creates a new file and creates a filestream
    fs.Write(data, 0, data.Length);
    fs.Close();
    

    If you now inspect the file with a binary editor you will see it contains:

    0x01, 0x02, 0x03, 0x04
    

    There are many types of streams with different purposes (per example, the MemoryStream that I used in the examples, a binary stream that stores it's data into an array in memory) and a ton of classes that use streams for many things, in this case you have mixed the concepts of serialization and data storage using streams, those are two different things.