DotNet — Cheat Sheet

Tag: dotnet, csharp, clr, aspnetcore, garbage-collector, async

Livello: intermedio

TL;DR: Panoramica su runtime .NET, gestione memoria con Garbage Collector, programmazione asincrona, dependency injection e ASP.NET Core per colloqui tecnici.

Concetti chiave

CLR (Common Language Runtime) gestisce esecuzione, JIT compilation, type safety e allocazione memoria.

Il Garbage Collector opera a generazioni: Gen 0 per oggetti effimeri, Gen 1 come buffer, Gen 2 per lunga persistenza.

Large Object Heap (LOH) alloca oggetti oltre gli 85.000 byte senza compattazione automatica ordinaria.

Value type allocati su stack o inline nella struct; reference type allocati sempre sull'heap gestito.

async/await sfrutta una state machine generata dal compilatore per liberare thread del threadpool durante I/O.

ConfigureAwait(false) evita la cattura del SynchronizationContext, riducendo overhead e deadlock su runtime legacy.

I/O bound tasks usano async; CPU bound tasks usano Task.Run per delegare l'elaborazione a worker thread.

Dependency Injection integrata supporta tre cicli di vita: Transient (nuovo per chiamata), Scoped (per richiesta HTTP), Singleton (unico per processo).

Kestrel e pipeline middleware di ASP.NET Core processano le richieste HTTP in sequenza bidirezionale.

Span e Memory consentono slicing di memoria contigua a zero allocazioni.

Domande tipiche e risposte pronte

Q: Qual e' la differenza tra Task.Run e una chiamata async naturale?R: La chiamata async naturale sfrutta completamenti di I/O senza impegnare thread del pool, mentre Task.Run sottrae attivamente un worker thread per carichi CPU-bound.

Q: Quando usi Scoped rispetto a Transient nella Dependency Injection?R: Scoped per condividere lo stato nell'arco della singola richiesta HTTP come DbContext, Transient per servizi stateless leggeri e isolati.

Q: Come gestisci la finalizzazione delle risorse non gestite?R: Implemento il pattern IDisposable con Dispose per rilascio deterministico, affiancato da IAsyncDisposable se il rilascio richiede operazioni I/O.

Insidie e errori comuni

Usare .Result o .Wait() su Task provocando threadpool starvation o deadlock.

Trattenere riferimenti a eventi statici senza unsubscribe, causando memory leak su Gen 2.

Captive Dependency: iniettare un servizio Scoped dentro un Singleton, rendendo lo Scoped un Singleton de facto.

Allocare stringhe ripetutamente in cicli invece di usare StringBuilder o Span.

Dimenticare la deregistrazione di IDisposable creati manualmente fuori dal container DI.

Snippet e comandiC#

// Registrazione dei servizi in Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheManager, CacheManager>();

var app = builder.Build();

C#

// Esecuzione asincrona pulita senza bloccare il thread
public async Task<UserProfile> GetProfileAsync(int id, CancellationToken ct)
{
    var user = await _userRepo.FindByIdAsync(id, ct).ConfigureAwait(false);
    return user is null ? throw new NotFoundException() : _mapper.Map(user);
}

Bash

# Comandi CLI frequenti
dotnet new webapi -n ApiProject
dotnet build --configuration Release
dotnet run --launch-profile https

Da ripassare

[ ] Meccanismi di allocazione e soglie del Large Object Heap (LOH)

[ ] Differenza pratica tra ValueTask e Task

[ ] Pattern IAsyncDisposable e DisposeAsync

[x] Cicli di vita della Dependency Injection (Transient, Scoped, Singleton)

[x] Differenza tra struct (value type) e class (reference type)

Riferimenti

Microsoft .NET Documentation

Common Language Runtime (CLR) Specifications

Jarvis Interviews
Jarvis