È uscito il Corso Java Completo
Torna al blog

Cos'è C# e a cosa serve

Cos'è C#, come funziona con .NET multipiattaforma, cosa sono davvero LINQ e async/await, dove si usa tra gestionali, API e Unity, e i suoi limiti.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

8 min di lettura

A C# ci si arriva quasi sempre da una porta laterale: volevi fare un videogioco con Unity, oppure sei entrato in un'azienda che ha un gestionale da mantenere. Poi scopri che è un linguaggio molto più interessante della fama che si porta dietro. In questo articolo trovi cos'è, cosa lo distingue davvero da Java, dove si usa e quali sono i suoi limiti reali.

Cos'è

C# è un linguaggio a oggetti e fortemente tipizzato, compilato in un formato intermedio che viene eseguito dal runtime .NET — oggi open source e multipiattaforma.

È nato in Microsoft nel 2000, progettato da Anders Hejlsberg come risposta diretta a Java. La somiglianza iniziale era voluta, la divergenza successiva anche di più.

Se conosci Java, C# lo leggi già

Non è un modo di dire. Classi, interfacce, ereditarietà, garbage collector, un runtime che esegue codice intermedio: l'impianto è lo stesso.

public class Ordine
{
    public string Cliente { get; init; }
    public List<Articolo> Articoli { get; init; } = new();

    public decimal Totale() => Articoli.Sum(a => a.Prezzo);
}

Un programmatore Java capisce questo codice senza aver mai visto C#. La differenza vera non è nella sintassi di base: è nel ritmo con cui i due linguaggi si sono evoluti.

Microsoft ha aggiunto a C# funzionalità di linguaggio in modo molto più aggressivo, e alcune sono arrivate anni o decenni prima che in Java: le proprietà con getter e setter integrati, i tipi generici con i tipi reali a runtime, async/await nel 2012, i tipi nullable verificati dal compilatore, i record, il pattern matching esteso. Il risultato è che oggi C# scrive lo stesso programma in sensibilmente meno righe, e con costrutti più espressivi.

Due di queste funzionalità meritano una spiegazione a parte, perché sono quelle che davvero cambiano il modo in cui si scrive il codice.

LINQ: interrogare le collezioni come un database

LINQ (Language Integrated Query) è un insieme di operazioni sulle collezioni integrate nel linguaggio. In pratica: filtri, trasformi e raggruppi dati scrivendo cosa vuoi ottenere, non il ciclo che lo ottiene.

Senza LINQ, con i cicli espliciti:

var risultato = new List<string>();
foreach (var u in utenti)
{
    if (u.Attivo && u.Eta >= 18)
        risultato.Add(u.Email);
}
risultato.Sort();

Con LINQ:

var risultato = utenti
    .Where(u => u.Attivo && u.Eta >= 18)
    .OrderBy(u => u.Email)
    .Select(u => u.Email)
    .ToList();

Fin qui è leggibilità. Il colpo vero è che la stessa identica sintassi funziona su un database. Con Entity Framework, quella query non viene eseguita in memoria: viene tradotta in SQL e mandata al database, che restituisce solo le righe utili.

var attivi = await db.Utenti
    .Where(u => u.Attivo && u.Eta >= 18)
    .OrderBy(u => u.Email)
    .ToListAsync();
// diventa: SELECT ... FROM Utenti WHERE Attivo = 1 AND Eta >= 18 ORDER BY Email

Scrivi un'unica forma di interrogazione per liste in memoria, righe di un database, documenti XML e risposte di una API. È una delle poche idee di linguaggio degli ultimi vent'anni che è stata copiata praticamente ovunque.

async/await: attendere senza bloccare

Il secondo tratto distintivo. Un programma che chiama un database o una API esterna passa la maggior parte del tempo ad aspettare. Se durante quell'attesa il thread resta bloccato, un server con cento richieste contemporanee ha bisogno di cento thread fermi a non fare nulla — memoria sprecata e un limite duro alla scalabilità.

async/await risolve questo, e lo fa mantenendo il codice leggibile dall'alto verso il basso:

public async Task<Report> GeneraReportAsync(int clienteId)
{
    var cliente = await db.Clienti.FindAsync(clienteId);
    var ordini  = await db.Ordini.Where(o => o.ClienteId == clienteId).ToListAsync();
    var meteo   = await http.GetFromJsonAsync<Meteo>("https://api.esempio.it/meteo");

    return new Report(cliente, ordini, meteo);
}

Ogni await dice al runtime: "qui devo aspettare, libera il thread e riprendimi quando il risultato arriva". Il codice si legge come se fosse sequenziale, ma non blocca nulla. Prima di questo costrutto la stessa cosa si scriveva con callback annidate, ed era una delle principali fonti di codice illeggibile.

C# l'ha introdotto nel 2012 ed è poi stato adottato quasi identico da JavaScript, Python, Rust e altri. Se hai visto async/await altrove, è nato qui.

Dove si usa

AmbitoCosa ci si fa
Gestionali aziendaliERP, CRM, software su misura per aziende medio-grandi
API e backend webASP.NET Core, uno dei framework server più veloci in assoluto
VideogiochiUnity: motore usato da una quota enorme di giochi indie e mobile
Applicazioni desktopWPF e WinForms su Windows, ancora molto presenti in azienda
Servizi cloudIntegrazione nativa e molto curata con Azure

Unity merita una nota. Per moltissime persone C# è il linguaggio in cui scrivono script per un gioco, non quello di un backend. Sono due mondi che condividono la sintassi ma quasi nient'altro: Unity ha un proprio ciclo di vita, propri vincoli sul garbage collector e una versione del runtime che spesso è indietro rispetto a .NET. Saper scrivere script per Unity non significa automaticamente saper scrivere un servizio ASP.NET, e viceversa.

L'equivoco da sfatare: non è "solo Windows"

È il punto su cui trovi ancora informazioni vecchie ovunque.

Fino al 2016 C# girava sul .NET Framework, legato a Windows. Poi Microsoft ha rilasciato .NET Core, open source e multipiattaforma, e dal 2020 le due linee sono confluite in .NET, punto. Da anni la situazione è questa:

dotnet new webapi -o MiaApi     # su Linux, macOS o Windows
cd MiaApi
dotnet run

Lo sviluppi su macOS, lo metti in un container Linux con Docker, lo orchestri con Kubernetes. Le immagini ufficiali sono Linux, gli strumenti a riga di comando funzionano ovunque, la maggior parte delle installazioni in produzione oggi gira su Linux. "C# è solo per Windows" è un'affermazione con dieci anni di ritardo — e va ricordato che sopravvive anche perché una parte di codice aziendale reale è ancora ferma sul vecchio .NET Framework, che invece su Windows resta.

I limiti, detti onestamente

Le pratiche restano legate all'ecosistema Microsoft. Il linguaggio è aperto, ma il percorso standard passa da Visual Studio, Azure, Entity Framework, la documentazione Microsoft. Funziona bene finché stai dentro; fuori dal solco tracciato trovi meno alternative rispetto a quanto succede nei mondi Java o JavaScript.

Nel web italiano è meno presente di quanto meriterebbe. Le agenzie e le startup qui lavorano soprattutto con JavaScript, PHP e Python. C# si trova nelle software house che fanno gestionali, nelle aziende manifatturiere, nel settore industriale e bancario. Ottime posizioni, ma un bacino diverso: se il tuo obiettivo è entrare in una startup web, C# non è la via più corta.

La superficie del linguaggio è ampia. Dodici anni di aggiunte hanno lasciato più modi di fare la stessa cosa. Leggere codice C# scritto in epoche diverse richiede di conoscere anche gli stili vecchi.

Il garbage collector resta un vincolo nei giochi. In Unity, allocare oggetti dentro il ciclo di aggiornamento produce micro-scatti quando il collettore interviene. È il primo problema di prestazioni che incontra chi sviluppa giochi.

Errori comuni che fanno perdere ore

Chiamare .Result o .Wait() su un metodo asincrono. È l'errore numero uno: in certi contesti blocca l'applicazione in un deadlock senza alcun messaggio d'errore. La regola è: una volta che entri nell'asincrono, resti asincrono fino in cima.

var dati = ServizioAsync().Result;   // rischio di blocco totale
var dati = await ServizioAsync();    // corretto

Dimenticare che LINQ è pigro. Where e Select non eseguono nulla finché non chiedi i risultati. Se enumeri la stessa query tre volte, la query al database parte tre volte. ToList() una volta sola, e poi lavori su quella.

Il problema delle query N+1 con Entity Framework. Carichi cento ordini e poi accedi al cliente di ciascuno: parte una query iniziale più cento query separate. Si risolve con Include, ma bisogna sapere che sta succedendo — ed è il motivo per cui conviene guardare l'SQL generato.

Ignorare i reference type nullable. C# moderno può avvisarti a compilazione dei riferimenti potenzialmente nulli. Molti disattivano l'avviso perché è rumoroso e si ritrovano gli stessi errori a runtime.

Confondere struct e class. Il primo è un tipo valore e viene copiato; il secondo è un tipo riferimento. Modificare una copia credendo di modificare l'originale è un classico.

In sintesi

C# è nato come risposta a Java e oggi è il linguaggio che si è evoluto più in fretta dei due. Stessa famiglia, ritmo diverso.

LINQ e async/await sono ciò che lo distingue davvero: interrogare collezioni e database con la stessa sintassi, e gestire le attese senza bloccare i thread, mantenendo il codice leggibile.

Non è più solo Windows da quasi dieci anni. .NET è open source, gira su Linux e nei container, e in produzione è lì che sta.

È forte dove sono i gestionali, le API aziendali e Unity, meno nel mondo startup e web italiano: vale la pena saperlo prima di sceglierlo.

Per il confronto con il suo parente stretto vedi cos'è Java; per collocarlo tra tutti gli altri, la mappa completa è in tutti i linguaggi di programmazione. Se stai ancora decidendo da dove partire, leggi quale linguaggio imparare nel 2026, e per mettere subito in pratica quello che studi ci sono progetti per imparare a programmare.