November 15, 2025 (5mo ago) — last updated February 5, 2026 (2mo ago)

Classi vs Struct: Quando scegliere

Scopri quando usare classi (riferimento) o struct (valore): impatto su memoria, performance, mutabilità e regole pratiche per un design più pulito.

← Back to blog
Cover Image for Classi vs Struct: Quando scegliere

Decidere tra classi e struct riguarda meno la sintassi e più la semantica. La domanda chiave è se serve semantica per valore (copia dei dati) o semantica per riferimento (identità condivisa). Questa guida spiega i compromessi su memoria, performance e mutabilità e fornisce regole pratiche per scegliere il tipo giusto.

Classi vs Struct: Quando usare ciascuna

Sommario: Confronta classi e struct—valore vs riferimento, memoria, performance e linee guida di design per scegliere il tipo giusto e scrivere codice efficiente.

Introduzione

Decidere tra classi e struct riguarda meno la sintassi e più la semantica. La domanda chiave è se serve semantica per valore (copia dei dati) o semantica per riferimento (identità condivisa). Questa scelta influisce su memoria, performance, mutabilità e architettura del codice. In questa guida spiego i compromessi principali e propongo regole pratiche per scegliere il tipo giusto.

La distinzione fondamentale: semantica per valore vs per riferimento

Un programmatore che confronta blocchi di codice per classi e strutture su un grande schermo.

Se si elimina la sintassi del linguaggio, la differenza è semplice: tipi per valore contro tipi per riferimento. Pensa a una struct come a un blocco note fotocopiato: dai a qualcuno le tue note e ottengono la loro copia. Possono modificarla senza cambiare l’originale. Questa è la semantica per valore. Una classe è come un documento condiviso: invii un link e tutti modificano lo stesso oggetto. Questa è la semantica per riferimento.

Differenze chiave a colpo d’occhio

CaratteristicaStruct (valore)Class (riferimento)
Passaggio datiI dati vengono copiatiPassato un riferimento
AllocazioneSpesso inline o sullo stackAllocati sull’heap
Ciclo di vitaCopie effimereIstanza condivisa, durata variabile
IdentitàUguale se i dati sono ugualiIdentità distinta dall’uguaglianza dei dati
EreditarietàDi solito noSupporta ereditarietà e polimorfismo
Uso principaleValori piccoli e auto-contenutiEntità complesse con stato e comportamento

Questi principi influenzano latenza, uso della memoria e correttezza. Scegliere con criterio rende il codice più prevedibile e manutenibile.

Come l’allocazione della memoria impatta le performance

Un diagramma che mostra la differenza tra allocazione di memoria su stack e heap.

Lo stack e l’heap determinano gran parte dell’impatto sulle performance.

Lo stack: veloce e prevedibile

Lo stack è una regione LIFO dove i dati locali vengono pushati e poppati. Allocare sullo stack è molto economico perché è solo aritmetica di puntatori. Per tipi per valore piccoli, allocazione e deallocazione sono quasi gratuite.

L’heap: flessibile ma più costoso

L’heap permette agli oggetti di sopravvivere oltre la chiamata corrente, ma l’allocazione è più lenta e può attivare la garbage collection o richiedere deallocazione manuale. I tipi per riferimento introducono un’ulteriore indirezione: lo stack contiene un puntatore ai dati sull’heap. Allocazioni ripetute sull’heap aumentano la pressione sul GC e possono causare pause nei runtime gestiti1.

Esempio in C#

// Value Type - lives inline (often on the stack)
public struct PointStruct {
    public int X;
    public int Y;
}

// Reference Type - object lives on the heap
public class PointClass {
    public int X;
    public int Y;
}

public void ProcessPoints() {
    PointStruct p1 = new PointStruct { X = 10, Y = 20 };
    PointClass p2 = new PointClass { X = 10, Y = 20 };
}

In loop stretti, migliaia di allocazioni sull’heap per piccoli oggetti possono aumentare significativamente l’attività del GC; le struct in array spesso ottengono una località di cache molto migliore e riducono la pressione sul GC2.

Come i linguaggi trattano classi e struct

Schermo diviso che mostra snippet di codice di diversi linguaggi di programmazione, illustrando classi e strutture.

I linguaggi enfatizzano default diversi. La scelta dipende dagli idiomi del linguaggio e dalle esigenze di performance.

C++: struct e class quasi identiche

In C++, struct e class sono quasi la stessa cosa; l’unica differenza tecnica è l’accesso di default. Usa struct per aggregati semplici e class per tipi incapsulati con comportamento complesso3.

C#: separazione chiara valore/riferimento

C# rende la distinzione esplicita: struct è un tipo per valore, class è un tipo per riferimento. Le struct sono ideali per valori piccoli e immutabili come coordinate o colori.

Swift: preferire i tipi per valore

Swift incoraggia un approccio orientato ai valori. Le linee guida favoriscono struct come default e riservano class ai casi che richiedono semantica per riferimento, come stato condiviso mutabile o interoperabilità con Objective-C4.

Rust: ownership e sicurezza

Rust usa struct con un modello di ownership e borrowing per offrire sicurezza della memoria senza garbage collector. Le regole del compilatore evitano molti bug di stato condiviso a compile-time5.

struct Player {
    username: String,
    level: u32,
    is_active: bool,
}

impl Player {
    fn level_up(&mut self) {
        self.level += 1;
    }
}

L’approccio di Rust offre controllo sulla memoria con garanzie a compile-time.

Quando scegliere una struct per migliorare le performance

Scegli una struct quando il tipo è piccolo, auto-contenuto e trattato come un valore invece che come un’identità. Candidati tipici:

  • Punti geometrici (Point2D)
  • Valori di colore (RGB/RGBA)
  • Piccoli payload di configurazione
  • Input leggeri per calcoli

I benefici includono meno allocazioni sull’heap, migliore località di cache e minore pressione sul GC. La migliore località di cache può migliorare significativamente il throughput in loop intensivi perché l’accesso alla memoria diventa più favorevole alla CPU2.

Quando scegliere una class per modellare comportamenti complessi

Scegli una class quando un oggetto ha un’identità stabile, stato mutabile condiviso o quando serve ereditarietà o gestione complessa del ciclo di vita. Candidati tipici:

  • Profili utente o entità di dominio
  • Oggetti di connessione a database o rete
  • Servizi e manager che coordinano stato

Le classi sono la base di molti pattern orientati agli oggetti. Ereditarietà e polimorfismo aiutano a modellare relazioni e comportamenti complessi.

Checklist decisionale: struct vs class

ConsiderazioneUsa struct (valore)Usa class (riferimento)
IdentitàI dati sono l’identitàL’oggetto ha un’identità unica
MutabilitàImmutabile o stato piccoloStato condiviso e mutabile
ComportamentoLogica semplice legata ai datiInterazioni e comportamenti complessi
Ciclo di vitaBreve, ambito localeLunga durata, application-wide
CondivisioneSicuro da copiareDeve essere condiviso per riferimento

Domande comuni

Una struct può avere metodi?

Sì. Linguaggi moderni come C#, Swift e Rust permettono alle struct di avere metodi, inizializzatori e conformità a protocolli o interfacce. La differenza principale resta il modo in cui vengono copiate e passate.

Le struct sono sempre più veloci?

No. Le struct piccole spesso superano gli oggetti allocati sull’heap, ma le struct grandi possono diventare costose da copiare. Misura sempre: profila carichi di lavoro reali prima di cambiare la modellazione dei tipi.

Le struct supportano l’ereditarietà?

Di solito no. Le struct raramente supportano ereditarietà, ma molti linguaggi permettono alle struct di implementare interfacce o protocolli, favorendo la composizione al posto dell’ereditarietà.

Q&A pratiche

Quando rifattorizzare una class in una struct?

Rifattorizza quando il tipo è un valore piccolo e immutabile senza identità unica, e vuoi ridurre allocazioni sull’heap e rendere la semantica più chiara.

Come ridurre le pause del GC nei linguaggi gestiti?

Riduci le allocazioni brevi sull’heap preferendo struct per valori piccoli, riutilizza oggetti con pool e misura il comportamento del GC sotto carico1.

Regola più semplice per decidere

Se l’oggetto rappresenta “un valore” usa una struct; se rappresenta “una cosa con identità” usa una class.

Tre Q&A concise (sommario finale)

Q: Passare alle struct migliora sempre la velocità?

A: No. Le struct aiutano per valori piccoli e frequenti, ma possono peggiorare le cose se sono grandi e copiate spesso. Profila prima di cambiare.

Q: Le struct aiutano la sicurezza del codice?

A: Sì. La semantica per valore riduce i bug dovuti a stato condiviso e mutazioni accidentali.

Q: Quando preferire una class?

A: Quando servono identità, ciclo di vita di lunga durata o ereditarietà e polimorfismo.


Presso Clean Code Guy aiutiamo i team a rifattorizzare per scalabilità e manutenibilità. Scopri di più su https://cleancodeguy.com.

1.
Microsoft Docs. “Garbage collection performance and tuning.” https://learn.microsoft.com/dotnet/standard/garbage-collection/
2.
Ulrich Drepper. “What Every Programmer Should Know About Memory.” https://people.freebsd.org/~lstewart/articles/cpumemory.pdf
3.
cppreference.com. “class vs struct.” https://en.cppreference.com/w/cpp/language/class
4.
Apple. “Structures and Classes” (Swift Programming Language). https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
5.
The Rust Programming Language. “Ownership.” https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
← Back to blog
🙋🏻‍♂️

L'AI scrive codice.
Tu lo fai durare.

Nell'era dell'accelerazione AI, il codice pulito non è solo una buona pratica — è la differenza tra sistemi che si scalano e codebase che collassano sotto il loro stesso peso.