Tag: csharp, dotnet, clr, linq, async, oop
Livello: intermedio
TL;DR: Linguaggio fortemente tipizzato e orientato agli oggetti per runtime .NET. Gestione memoria tramite CLR e Garbage Collector con supporto nativo per asincronia e LINQ.
Compilazione in Common Intermediate Language (CIL/IL) eseguito dalla Common Language Runtime (CLR).
Value types memorizzati su stack (struct, primitivi, enum); reference types allocati sull'heap (class, interface, delegate).
Garbage Collector generazionale (Gen 0, Gen 1, Gen 2 e Large Object Heap) per il recupero automatico della memoria.
Asincronia cooperativa basata su Task e state machine generata dal compilatore tramite async/await.
LINQ (Language Integrated Query) unifica l'interrogazione di collezioni in-memory e sorgenti esterne (IQueryable vs IEnumerable).
Pattern matching avanzato (type pattern, relational pattern, property pattern) per ridurre type casting esplicito.
Record types immutabili per definizione con supporto a with expressions e value equality automatica.
Nullable Reference Types per rilevare potenziali NullReferenceException a tempo di compilazione.
Q: Qual è la differenza sostanziale tra struct e class in C#?R: La struct è un value type memorizzato nello stack o inline, copiato per valore. La class è un reference type allocato sull'heap, gestito dal Garbage Collector e passato per riferimento.
Q: Come funziona il costrutto using e quale interfaccia richiede?R: Richiede l'interfaccia IDisposable. Garantisce la chiamata automatica al metodo Dispose() per liberare risorse non gestite, traducendosi internamente in un blocco try/finally.
Q: Qual è la differenza tra IEnumerable<T> e IQueryable<T>?R: IEnumerable<T> esegue i filtri in memoria nell'applicazione client. IQueryable<T> traduce l'espressione (Expression Tree) nella query nativa della sorgente, ad esempio SQL con Entity Framework.
Q: Perché evitare async void tranne che negli event handler?R: async void non restituisce un Task tracciabile. Le eccezioni non gestite non possono essere catturate dal chiamante e causano il crash del processo.
Boxing e unboxing non intenzionali convertendo value types a object o interfacce, con overhead sull'heap.
Chiamare .Result o .Wait() su Task asincroni generando deadlock sul SynchronizationContext.
Mancata cancellazione della sottoscrizione a eventi con delegate, che causa memory leak impedendo la deallocazione via GC.
Modifica concorrente di collezioni standard non thread-safe invece di utilizzare System.Collections.Concurrent.
Closure capture errata di variabili dentro lambda o cicli for/foreach.
// Esempio record e pattern matching
public record User(int Id, string Role, bool IsActive);
public decimal GetDiscount(User user) => user switch
{
{ IsActive: false } => 0m,
{ Role: "Admin" } => 0.30m,
{ Role: "Customer" } => 0.10m,
_ => 0.05m
};
C#
// Gestione asincrona corretta con CancellationToken
public async Task<string> FetchDataAsync(HttpClient client, string url, CancellationToken ct)
{
using var response = await client.GetAsync(url, ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(ct);
}
[ ] Meccanismo di allocazione LOH (Large Object Heap) e compattazione
[ ] Differenze tra ValueTask<T> e Task<T> per ottimizzazione allocazioni
[ ] Funzionamento interno degli Expression Trees in LINQ
[x] Utilizzo di IDisposable e pattern Dispose standard
[x] Sintassi e semantica di async/await