C# > Data Access > File I/O > Binary Files

Writing and Reading a Custom Object to/from a Binary File

This snippet demonstrates how to write and read a custom object to/from a binary file. It uses BinaryFormatter, which serializes objects to binary format, enabling the storage and retrieval of complex data structures.

Code Snippet: Custom Object Binary Serialization

This code defines a 'Person' class marked with the '[Serializable]' attribute, which is required for BinaryFormatter to serialize it. It creates a Person object, serializes it using a BinaryFormatter, and writes it to a binary file. Then, it reads the object back from the file, deserializes it using BinaryFormatter, and displays its properties. Note the explicit cast to (Person) after deserialization. Exception handling is important to catch both IOExceptions and SerializationExceptions.

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

public class ObjectBinaryFileExample
{
    public static void Main(string[] args)
    {
        string filePath = "person.bin";

        // Writing the Person object to the binary file
        Person person = new Person("Alice", 30);

        try
        {
            using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fileStream, person);
                Console.WriteLine("Person object serialized and written to file.");
            }
        }
        catch (IOException e)
        {
            Console.WriteLine("An error occurred during writing: " + e.Message);
        }

        // Reading the Person object from the binary file
        try
        {
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                Person loadedPerson = (Person)formatter.Deserialize(fileStream);

                Console.WriteLine("Person object deserialized from file:");
                Console.WriteLine("Name: " + loadedPerson.Name);
                Console.WriteLine("Age: " + loadedPerson.Age);
            }
        }
        catch (IOException e)
        {
            Console.WriteLine("An error occurred during reading: " + e.Message);
        }
        catch (System.Runtime.Serialization.SerializationException e)
        {
            Console.WriteLine("An error occurred during deserialization: " + e.Message);
        }
    }
}

Concepts Behind the Snippet

Serialization is the process of converting an object's state into a stream of bytes. Deserialization is the reverse process of converting a stream of bytes back into an object. The BinaryFormatter class is a built-in .NET class that provides serialization and deserialization capabilities using a binary format. The [Serializable] attribute is mandatory for the class to be serialized. FileStream is used for interacting with the file. Remember the object needs to be marked with the [Serializable] attribute.

Real-Life Use Case

Storing application configuration data, persisting game state, transferring objects across a network, or caching data for later use are all examples. For example, you could store the user's preferred window size, font settings, and other application preferences in a binary file using object serialization.

Best Practices

Use the 'using' statement for FileStream to ensure proper file closure. Handle IOException exceptions. Be aware of the security implications of using BinaryFormatter, as it can be vulnerable to deserialization attacks (see 'Cons' section). Consider using more secure serialization methods like JSON or Protocol Buffers if security is a concern. For larger, more complex objects, consider using more efficient serialization libraries. Always validate the data after deserialization to prevent unexpected behavior.

Interview Tip

Understand the concept of serialization and deserialization. Know the difference between BinaryFormatter and other serialization methods. Be prepared to discuss the security risks associated with BinaryFormatter and alternative approaches. Understand how to mark a class as serializable using the [Serializable] attribute.

When to Use Object Serialization

When you need to persist complex data structures, when you need to transfer objects between applications, or when you need to cache data for later use. It's a convenient way to store and retrieve objects without having to manually write code to handle each individual property.

Memory Footprint

The memory footprint depends on the size and complexity of the object being serialized. BinaryFormatter generally produces a compact binary representation, but the size can still be significant for large objects with many properties. Consider using compression techniques to further reduce the file size. The larger the object the more memory is used to write to the binary file.

Alternatives

JSON (JavaScript Object Notation) is a human-readable text-based format that is widely used for data interchange. XML (Extensible Markup Language) is another text-based format that is more verbose than JSON. Protocol Buffers is a binary serialization format developed by Google that is known for its speed and efficiency. Data Contract Serializer is another option built into the .NET framework. Choose the alternative that best fits your project requirements for security, performance, and readability.

Pros

Easy to use for simple objects. Handles complex data structures automatically. Binary format generally produces smaller files than text-based formats.

Cons

BinaryFormatter can be vulnerable to deserialization attacks if the input data is not carefully validated. This can allow an attacker to execute arbitrary code on the server. Version compatibility can be an issue if the class definition changes between serialization and deserialization. Not human-readable, making it difficult to debug.

FAQ

  • Why do I need the [Serializable] attribute?

    The [Serializable] attribute tells the .NET runtime that the class can be serialized. Without it, the BinaryFormatter will throw an exception indicating that the class is not marked as serializable. The CLR checks for this attribute to ensure that the developer has explicitly opted-in to serialization, as it can have security and versioning implications.
  • What is a deserialization attack, and how can I prevent it?

    A deserialization attack occurs when an attacker can inject malicious data into the serialized stream, which can then be used to execute arbitrary code when the object is deserialized. To prevent this, avoid using BinaryFormatter with untrusted input. Consider using more secure serialization methods like JSON or Protocol Buffers, and always validate the deserialized data to ensure it is what you expect.