C# — Cheat Sheet

Tag: csharp, dotnet, clr, oop, linq, memory-management

Livello: intermedio

TL;DR: Riferimento rapido su memoria, runtime .NET, OOP e costrutti asincroni per colloqui tecnici su C#.

Concetti chiave

I value type risiedono nello stack o inline, i reference type risiedono nello heap gestito dal Garbage Collector.

struct definisce un value type non ereditabile; class definisce un reference type polimorfico.

record introduce tipi immutabili per default con uguaglianza basata sui valori invece che sui riferimenti.

Il Garbage Collector organizza la memoria in Generazioni 0, 1, 2 e Large Object Heap (LOH).

IDisposable e l'istruzione using gestiscono il rilascio deterministico di risorse non gestite.

async/await trasforma il metodo in una macchina a stati gestita da Task e SynchronizationContext.

LINQ adotta esecuzione ritardata (deferred execution) tramite l'interfaccia IEnumerable<T>.

Span<T> e Memory<T> offrono viste contigue su memoria allocata senza generare copie o allocazioni heap.

I delegati sono puntatori a funzione type-safe; gli eventi aggiungono protezione sull'invocazione esterna.

string e un reference type immutabile memorizzato internamente con tabella hash (string interning).

Domande tipiche e risposte pronte

Q: Qual e la differenza tra ref e out nei parametri di metodo?R: Con ref la variabile deve essere inizializzata prima del passaggio, mentre con out l'inizializzazione avviene obbligatoriamente dentro il metodo chiamato.

Q: Quando conviene usare ConfigureAwait(false) in una chiamata asincrona?R: Lo uso nelle librerie e nel codice backend per evitare il riaggancio del contesto originale e prevenire deadlock o overhead.

Q: Come gestisci il pattern Dispose in classi derivate con risorse non gestite?R: Implemento il pattern Dispose(bool disposing), chiamo GC.SuppressFinalize(this) e rilascio le risorse native anche tramite finalizzatore.

Q: Qual e la differenza tra IEnumerable<T> e IQueryable<T>?R: IEnumerable<T> valuta i dati in-memory lato client, mentre IQueryable<T> traduce le expression tree in query native su database.

Insidie e errori comuni

Catturare closure errate all'interno di loop foreach o espressioni lambda.

Eseguire chiamate sincrone bloccanti su task asincroni tramite .Result o .Wait().

Modificare una collection mentre viene iterata con un blocco foreach.

Mancata invocazione di GC.SuppressFinalize nel metodo Dispose.

Allocazioni nascoste di boxing e unboxing quando si passano value type a interfacce generiche.

Snippet e comandiC#

// Record immutabile e pattern matching
public record Employee(string Name, decimal Salary);

public static decimal EvaluateBonus(Employee emp) => emp switch
{
    { Salary: > 50000m } => 5000m,
    { Salary: > 30000m } => 3000m,
    _ => 1000m
};

C#

// Gestione deterministica con using declaration
public void ProcessFile(string path)
{
    using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
    using var reader = new StreamReader(stream);
    var content = reader.ReadToEnd();
}

Da ripassare

[ ] Meccanismi di allocazione nel Large Object Heap per array superiori a 85.000 byte.

[ ] Macchina a stati generata dal compilatore per async/await.

[x] Differenza semantica tra ref struct e struct standard.

[x] Regole di visibilita dei modificatori di accesso protected internal e private protected.

Jarvis Interviews
Jarvis