CodeGym /Corsi /C# SELF /Sequenza di azioni ed esempi: serializzazione degli ogget...

Sequenza di azioni ed esempi: serializzazione degli oggetti

C# SELF
Livello 43 , Lezione 1
Disponibile

1. Introduzione

Siamo già abituati a creare oggetti nei nostri programmi. Ricordate, stiamo per iniziare a studiare classi e oggetti più a fondo, ma già ora capiamo che variabili, liste, anche semplici stringhe — non sono roba casuale, sono delle "entità" nel nostro programma. Per esempio, possiamo creare una variabile int age = 30; o string name = "Vasia";. Ma cosa succede se dobbiamo salvare su file le informazioni su un intero utente che ha nome, età, indirizzo, una lista di libri preferiti e molto altro?

Immagina: stai scrivendo un gioco. Hai un oggetto Player con tante caratteristiche: salute, livello, inventario (lista di item), coordinate sulla mappa e così via. Il giocatore gioca, migliora, trova artefatti fighi. E poi decide di uscire dal gioco. Cosa succede? Tutti i dati sulle sue avventure che erano in memoria spariscono! Che tristezza! Per evitare questo dobbiamo salvare lo stato dell'oggetto Player su file, e quando il giocatore torna, caricarlo di nuovo.

Ed è qui che entra in scena la serializzazione. Si occupa proprio di costruire un ponte tra gli oggetti "vivi" in memoria e i dati "morti", ma persistenti, su disco.

2. Processo di serializzazione (da oggetto a file)

Analizziamo come avviene questa "magia" che trasforma un oggetto in byte che possono essere scritti su file.

Immaginate che abbiamo questa classe Book (Libro):


// Questo è il nostro "disegno" o "piano" per creare oggetti libro
public class Book
{
    // Proprietà del libro
    public string Title { get; set; } // Titolo del libro
    public string Author { get; set; } // Autore
    public int Year { get; set; } // Anno di pubblicazione

    // Costruttore - metodo speciale per creare nuovi oggetti Book
    public Book(string title, string author, int year)
    {
        Title = title;
        Author = author;
        Year = year;
    }

    // Metodo per stampare comodamente le informazioni sul libro (non obbligatorio per la serializzazione, ma utile)
    public void DisplayInfo()
    {
        Console.WriteLine($"Titolo: {Title}, Autore: {Author}, Anno: {Year}");
    }
}

Passo 1: Creare l'oggetto da serializzare.

Prima di tutto ci serve l'oggetto che vogliamo salvare. Per esempio abbiamo creato un'istanza di Book:

Book myFavoriteBook = new Book("Avtostopom po Galaktike", "Duglas Adams", 1979);

Questo oggetto myFavoriteBook è adesso nella memoria RAM.

Passo 2: Scelta dello strumento (serializer).

Non possiamo semplicemente "copiare" l'oggetto su disco. Il computer non capisce direttamente gli oggetti nei file — ha bisogno di byte. Serve uno strumento speciale — il serializer. Il suo compito è scomporre il nostro oggetto nelle sue parti (le sue proprietà: Title, Author, Year) e trasformare queste parti in una sequenza di byte o in una stringa di testo (per esempio JSON o XML).

Oggi non entriamo nelle implementazioni specifiche — pensiamolo solo come una "scatola-convertitore".

Passo 3: Trasformare l'oggetto in uno stream di dati.

Il serializer prende il nostro oggetto myFavoriteBook, guarda le sue proprietà (Title, Author, Year) e converte ciascuna in un formato che si può scrivere. Tutti questi byte (o caratteri di testo) vengono raccolti in un unico flusso di dati — una lunga "striscia" di informazioni.

Passo 4: Scrivere lo stream su file.

Ora che abbiamo questa "striscia di byte", usiamo i nostri vecchi amici FileStream e, se serve, StreamWriter (se scegliamo un formato testuale come JSON) o solo FileStream (per dati puramente binari), per scrivere questo flusso su disco.

3. Processo di deserializzazione (da file a oggetto)

Passo 1: Lettura dello stream di dati dal file.

Usiamo di nuovo FileStream e se necessario StreamReader (se è un formato testuale) per leggere il contenuto del file. I dati arrivano come una "striscia" di byte o testo.

Passo 2: Scelta dello strumento (deserializer).

Serve lo strumento inverso — il deserializer. Deve sapere come interpretare i byte/testo ricevuti e ricostruire correttamente la struttura dell'oggetto. È molto importante: per deserializzare si usa lo stesso tipo di serializer (e di solito la stessa libreria) che si è usata per serializzare, altrimenti il "costruttore" non capirà le istruzioni di ricomposizione.

Passo 3: Trasformare lo stream di nuovo in oggetto.

Il deserializer legge i dati, capisce dove sta Title, poi Author, poi Year, e sulla base di questo crea un nuovo oggetto Book in memoria, popolando le sue proprietà.

Passo 4: Ottenere l'oggetto pronto.

Voilà! Abbiamo di nuovo un oggetto Book completo in memoria, con cui possiamo lavorare.

Esempio: Salviamo il nostro "Super-Libro" manualmente (per capire il concetto)

Per ora non useremo librerie specializzate: facciamo la serializzazione e deserializzazione più semplice "manuale" con StreamWriter e StreamReader. Questo aiuta a capire il principio.

Il nostro oggetto 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($"Titolo: \"{Title}\", Autore: {Author}, Anno: {Year}");
    }
}

Serializzazione manuale: metodo SaveBookToTextFile

Creiamo un metodo che salvi le proprietà del libro in un file di testo, una proprietà per riga.

void SaveBookToTextFile(Book book, string filePath)
{
    using var writer = new StreamWriter(filePath);
    writer.WriteLine(book.Title);
    writer.WriteLine(book.Author);
    writer.WriteLine(book.Year);
}

Cosa succede? Apriamo StreamWriter e scriviamo in sequenza Title, Author, Year — questo è il nostro schema di serializzazione più semplice.

Se esegui il codice, il contenuto del file sarà così:


Avtostopom po Galaktike
Duglas Adams
1979

Deserializzazione manuale: metodo LoadBookFromTextFile

Scriviamo un metodo che legga i dati e costruisca un nuovo oggetto 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 usiamo questi metodi in Main:

//creiamo l'oggetto
var myBook = new Book("Avtostopom po Galaktike", "Duglas Adams", 1979);
string filePath = "my_favorite_book.txt";

//lo salviamo su file
SaveBookToTextFile(myBook, filePath);

//leggiamo dal file
Book loadedBook = LoadBookFromTextFile(filePath);

Cosa succede in LoadBookFromTextFile? Apriamo StreamReader e leggiamo nello stesso ordine le righe: prima il titolo, poi l'autore, poi l'anno e lo convertiamo con int.Parse. Dopo creiamo una nuova istanza di Book.

In pratica è bene aggiungere controlli (File.Exists) e gestione degli errori con try-catch, ma qui ci concentriamo sull'idea di base.

Perché la serializzazione "manuale" è problematica (e perché servono le librerie)?

  • Molto codice manuale. Ogni proprietà va scritta e poi letta. Se gli oggetti e i campi sono tanti, il codice cresce molto.
  • Fragilità ai cambiamenti. Aggiunto un nuovo campo Pages (int)? Bisogna cambiare sia la scrittura che la lettura, e tenere strettamente l'ordine.
  • Strutture complesse. Collezioni e oggetti annidati (per esempio List<Chapter>) trasformano il codice in "spaghetti".
  • Formati ed efficienza. Il formato testuale è semplice, ma non sempre compatto o sicuro; per dati binari bisogna lavorare manualmente con i byte, BinaryWriter/BinaryReader ecc.
  • Mancanza di metadati. Il nostro file non "sa" che la prima riga è Title e la terza è Year. I serializer specializzati possono salvare metadati e essere più resistenti ai cambiamenti del modello.

Proprio per questo nei progetti reali si usano librerie serializer pronte, che sanno automaticamente decomporre/ricomporre oggetti, lavorare con JSON, XML e formati binari e gestire meglio le evoluzioni dei modelli. Ne parleremo nella prossima lezione!

Applicazioni pratiche: a cosa serve la serializzazione?

  • Salvataggio e caricamento dei dati. Impostazioni, stati di gioco, configurazioni.
  • Trasmissione dei dati in rete. Scambio di oggetti complessi tra servizi (spesso in JSON).
  • Caching. Riutilizzo rapido di dati già ottenuti.
  • Logging di oggetti complessi. Utile per debug e audit.
  • Copia profonda degli oggetti. Serializzazione + deserializzazione come modo per clonare un grafo di oggetti.

La serializzazione è uno dei pilastri del software moderno: dal salvataggio del progresso di un gioco al lavoro con web service — è ovunque!

2
Compito
C# SELF, livello 43, lezione 1
Bloccato
Creazione di un sistema di salvataggio primitivo
Creazione di un sistema di salvataggio primitivo
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION