1. Introdução
Já estamos acostumados a criar objetos nos nossos programas. Lembra, a gente logo vai começar a estudar classes e objetos mais a fundo, mas já agora entendemos que variáveis, listas, até strings simples — não são só coisas simples, já são umas "entidades" no nosso programa. Por exemplo, podemos criar a variável int age = 30; ou string name = "Vasya";. Mas e se a gente precisar salvar num arquivo a informação de um usuário inteiro, que tem nome, idade, endereço, lista de livros favoritos e muito mais?
Imagina: você tá escrevendo um jogo. Você tem um objeto Player com um monte de características: saúde, nível, inventário (lista de itens), coordenadas no mapa, etc. O jogador joga, upa, encontra artefatos legais. E aí ele decide sair do jogo. O que acontece? Todos os dados da aventura que estavam na memória vão sumir! Tristeza! Para isso não acontecer, precisamos salvar o estado do objeto Player em um arquivo, e quando o jogador voltar, carregá‑lo de volta.
E é aqui que entra a serialização. Ela é justamente a ponte entre objetos "vivos" na memória e dados "mortos", mas persistentes, no disco.
2. Processo de serialização (do objeto para o arquivo)
Vamos ver como acontece essa "mágica" de transformar um objeto em bytes que dá pra gravar num arquivo.
Imagine que temos essa classe Book (Livro):
// Este é nosso "projeto" ou "plano" para criar objetos-book
public class Book
{
// Propriedades do livro
public string Title { get; set; } // Título do livro
public string Author { get; set; } // Autor
public int Year { get; set; } // Ano de publicação
// Construtor - método especial para criar novos objetos Book
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
// Método para exibir info do livro (ainda não obrigatório para serializar, mas útil)
public void DisplayInfo()
{
Console.WriteLine($"Título: {Title}, Autor: {Author}, Ano: {Year}");
}
}
Passo 1: Criar o objeto para serializar.
Primeiro, claro, precisamos do objeto que queremos salvar. Por exemplo, criamos uma instância de Book:
Book myFavoriteBook = new Book("Autostop pelo Universo", "Douglas Adams", 1979);
Esse objeto myFavoriteBook agora está na memória RAM.
Passo 2: Escolher a ferramenta (o serializer).
Não dá pra simplesmente "copiar" o objeto pro disco. O computador não entende objetos diretamente em arquivos — ele precisa de bytes. Precisamos de uma ferramenta especial — o serializer. O trabalho dele é desmontar nosso objeto em partes (suas propriedades: Title, Author, Year) e transformar essas partes em uma sequência de bytes ou numa string de texto (por exemplo, JSON ou XML).
Hoje não vamos nos aprofundar em implementações específicas — só lembrar que é uma "caixa de transformação".
Passo 3: Converter o objeto em um fluxo de dados.
O serializer recebe nosso objeto myFavoriteBook, olha suas propriedades (Title, Author, Year) e converte cada uma para um formato gravável. Todos esses bytes (ou caracteres de texto) são reunidos num fluxo — uma "fita" longa de informação.
Passo 4: Gravar o fluxo no arquivo.
Agora que temos essa "fita de bytes", usamos os velhos conhecidos FileStream e, se precisar, StreamWriter (se for formato texto, tipo JSON) ou só FileStream (para dados binários), para escrever esse fluxo no disco.
3. Processo de desserialização (do arquivo para o objeto)
Passo 1: Ler o fluxo de dados do arquivo.
Usamos novamente FileStream e, se for texto, StreamReader, para ler o conteúdo do arquivo. Os dados chegam como uma "fita" de bytes ou texto.
Passo 2: Escolher a ferramenta (o desserializer).
Precisamos do instrumento inverso — o desserializer. Ele precisa saber interpretar os bytes/texto e recriar a estrutura do objeto corretamente. É muito importante: para desserializar normalmente usamos o mesmo tipo de serializer (e geralmente a mesma biblioteca) que foi usado pra serializar, senão o "construtor" não vai entender as instruções de montagem.
Passo 3: Converter o fluxo de volta em objeto.
O desserializer lê os dados, entende onde está Title, depois Author, depois Year, e com isso cria um novo objeto Book na memória, preenchendo suas propriedades.
Passo 4: Obter o objeto pronto.
Voilà! Temos de novo um objeto Book completo na memória, pronto pra usar.
Exemplo: Salvando nosso "Super-Livro" 'manualmente' (pra entender a ideia)
Por agora não vamos usar bibliotecas especializadas: vamos fazer a serialização e desserialização mais simples possível usando StreamWriter e StreamReader. Isso ajuda a entender o princípio.
Nosso objeto Book:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
public void DisplayInfo()
{
Console.WriteLine($"Título: \"{Title}\", Autor: {Author}, Ano: {Year}");
}
}
Serialização manual: método SaveBookToTextFile
Vamos criar um método que salva as propriedades do livro em um arquivo de texto, uma propriedade por linha.
void SaveBookToTextFile(Book book, string filePath)
{
using var writer = new StreamWriter(filePath);
writer.WriteLine(book.Title);
writer.WriteLine(book.Author);
writer.WriteLine(book.Year);
}
O que acontece? Abrimos um StreamWriter e escrevemos em sequência Title, Author, Year — esse é nosso esquema de serialização mais simples.
Se executar o código, o conteúdo do arquivo ficará assim:
Autostop pelo Universo
Douglas Adams
1979
Desserialização manual: método LoadBookFromTextFile
Agora um método que lê os dados e monta um novo objeto Book.
Book LoadBookFromTextFile(string filePath)
{
using var reader = new StreamReader(filePath);
string title = reader.ReadLine();
string author = reader.ReadLine();
int year = int.Parse(reader.ReadLine());
return new Book(title, author, year);
}
E usamos esses métodos no Main:
//criamos o objeto
var myBook = new Book("Autostop pelo Universo", "Douglas Adams", 1979);
string filePath = "my_favorite_book.txt";
//salvamos no arquivo
SaveBookToTextFile(myBook, filePath);
//lendo do arquivo
Book loadedBook = LoadBookFromTextFile(filePath);
O que acontece em LoadBookFromTextFile? Abrimos um StreamReader e lemos as linhas na mesma ordem: primeiro o título, depois o autor, depois o ano e convertemos com int.Parse. Depois criamos uma nova instância de Book.
Na prática vale adicionar checagens (File.Exists) e tratar erros com try-catch, mas aqui estamos focando na ideia principal.
Por que a serialização "manual" é ruim (e por que precisamos de bibliotecas)?
- Muito código manual. Cada propriedade precisa ser escrita e depois lida. Se houver muitos objetos e campos — o código cresce demais.
- Sensível a mudanças. Adicionou uma propriedade Pages (int)? Vai ter que alterar escrita e leitura e controlar rigorosamente a ordem.
- Estruturas complexas. Coleções aninhadas e objetos (por exemplo, List<Chapter>) transformam o código em "spaghetti".
- Formatos e eficiência. Formato texto é simples, mas nem sempre compacto e seguro; para dados binários você precisa manipular bytes manualmente, BinaryWriter/BinaryReader, etc.
- Sem metadados. Nosso arquivo não "sabe" que a primeira linha é Title e a terceira é Year. Serializers especializados podem salvar metadados e resistir melhor a mudanças no modelo.
É por isso que, em projetos reais, usam bibliotecas de serialização prontas, que automaticamente conseguem desmontar/montar objetos, trabalhar com JSON, XML e formatos binários e suportar mudanças nos modelos de forma mais robusta. Falaremos disso na próxima aula!
Aplicações práticas: pra que serve a serialização?
- Salvar e carregar dados. Configurações, estados de jogos, configurações.
- Transferir dados pela rede. Troca de objetos complexos entre serviços (frequentemente em JSON).
- Cache. Reuso rápido de dados obtidos anteriormente.
- Log de objetos complexos. Útil pra debugging e auditoria.
- Cópia profunda de objetos. Serialização + desserialização como forma de clonar um grafo de objetos.
A serialização é um dos pilares do software moderno: desde salvar progresso em jogos até trabalhar com web services — ela está em todo lugar!
GO TO FULL VERSION