Union Types in C# 15: insiemi chiusi di tipi con pattern matching esaustivo
#tech
spcnet.it/union-types-in-c-15-…
@informatica
Union Types in C# 15: insiemi chiusi di tipi con pattern matching esaustivo
Le union types sono finalmente arrivate in C#. Disponibili in anteprima a partire da .NET 11 Preview 2, C# 15 introduce la parola chiaveunionche consente di dichiarare un tipo che può contenere esattamente uno tra un insieme fisso di tipi, con conversioni implicite e pattern matching esaustivo garantito dal compilatore.È una delle funzionalità più richieste dalla community da anni, e capire come funziona — e soprattutto perché è stata progettata in questo modo — fa la differenza tra usarla correttamente o incappare negli stessi antipattern che esistevano prima.
Il problema che le union types risolvono
Prima di C# 15, quando un metodo doveva restituire uno tra diversi tipi possibili, le opzioni disponibili erano tutte imperfette:
object: nessun vincolo sui tipi effettivamente memorizzati; il chiamante doveva gestire logica difensiva per valori inattesi.- Interfacce marcatore o classi base astratte: più sicure, ma non “chiuse” — chiunque poteva implementare l’interfaccia o derivare dalla classe base, quindi il compilatore non poteva mai considerare l’insieme completo dei tipi possibili.
- Librerie di terze parti come
OneOf: funzionali, ma senza supporto diretto del compilatore per l’esaustività.Un caso tipico è il risultato di un’operazione che può restituire un valore di successo oppure un errore: si potrebbe usare
object, un’eccezione, oppure un tipo result wrapper. Nessuna di queste opzioni è soddisfacente perché manca la garanzia statale che tutti i casi siano gestiti.Le union types risolvono questi problemi dichiarando un insieme chiuso di “case types”: non devono essere correlati tra loro, nessun altro tipo può essere aggiunto, e il compilatore garantisce che le espressioni
switchche gestiscono la union siano esaustive — senza aver bisogno di un ramo_odefault.Sintassi di base
La dichiarazione è minimalista:public record class Cat(string Name); public record class Dog(string Name); public record class Bird(string Name); public union Pet(Cat, Dog, Bird);
Una sola riga dichiaraPetcome un nuovo tipo le cui variabili possono contenere unCat, unDogo unBird. Il compilatore fornisce conversioni implicite da ciascun case type:Pet pet = new Dog("Rex"); Console.WriteLine(pet.Value); // Dog { Name = Rex } Pet pet2 = new Cat("Whiskers"); Console.WriteLine(pet2.Value); // Cat { Name = Whiskers }
Il compilatore emette un errore se si cerca di assegnare un tipo che non fa parte dei case types. Questa è la garanzia fondamentale: l’insieme è veramente chiuso a livello di compilazione.Pattern matching esaustivo
Quando si usa un’istanza di un tipounionnon nulla, il compilatore conosce l’insieme completo dei case types, quindi un’espressioneswitchche li copre tutti è esaustiva — senza bisogno del_finale:string name = pet switch { Dog d => d.Name, Cat c => c.Name, Bird b => b.Name, };
Questo è il vantaggio principale: se in futuro si aggiunge un quarto case type aPet, ogni espressioneswitchche non lo gestisce produce un avviso del compilatore. I casi mancanti vengono rilevati in fase di compilazione, non a runtime.I pattern si applicano alla proprietà
Valuedella union, non alla union struct stessa. Questo “unwrapping” è automatico — si scriveDog de il compilatore verificaValueinternamente. Le eccezioni sonovare_, che si applicano al valore della union stessa.Per gestire il valore di default (null):
Pet pet = default; var description = pet switch { Dog d => d.Name, Cat c => c.Name, Bird b => b.Name, null => "nessun animale", }; // description è "nessun animale"Union con corpo e metodi helper
È possibile aggiungere membri helper alla union tramite un corpo, proprio come per qualsiasi altra dichiarazione di tipo. Un esempio pratico èOneOrMore<T>, utile per API che accettano sia un singolo elemento che una collezione:public union OneOrMore<T>(T, IEnumerable<T>) { public IEnumerable<T> AsEnumerable() => Value switch { T single => [single], IEnumerable<T> multiple => multiple, null => [] }; }
I chiamanti passano la forma che preferiscono, eAsEnumerable()normalizza il risultato:OneOrMore<string> tags = "dotnet"; OneOrMore<string> moreTags = new[] { "csharp", "unions", "preview" }; foreach (var tag in tags.AsEnumerable()) Console.Write($"[{tag}] "); // [dotnet] foreach (var tag in moreTags.AsEnumerable()) Console.Write($"[{tag}] "); // [csharp] [unions] [preview]
Si noti cheAsEnumerablegestisce esplicitamente il casonull: lo stato null predefinito della proprietàValueè maybe-null, quindi il compilatore richiede la gestione di questo caso per garantire la correttezza.Compatibilità con librerie esistenti e scenari avanzati
Per le librerie che già forniscono tipi union-like con proprie strategie di storage (come quelle basate suOneOf), C# 15 prevede un meccanismo di compatibilità: qualsiasi classe o struct con l’attributo[System.Runtime.CompilerServices.Union]viene riconosciuta come tipo union dal compilatore, purché segua il pattern base — costruttori pubblici a parametro singolo e proprietàValuepubblica.Per scenari ad alte prestazioni dove i case types includono tipi valore, le librerie possono implementare il pattern di accesso non-boxing aggiungendo una proprietà
HasValuee metodiTryGetValue. Il tipo union generato dal compilatore usaobject?internamente e quindi fa boxing dei tipi valore — per hot path critici conviene valutare i custom union types.Come provare le union types oggi
Le union types sono disponibili a partire da .NET 11 Preview 2. I passaggi per iniziare sono:
- Installare il .NET 11 Preview SDK
- Creare o aggiornare un progetto che punta a
net11.0- Impostare
<LangVersion>preview</LangVersion>nel file di progettoPoiché
UnionAttributeeIUnionnon sono ancora inclusi nel runtime nel Preview 2, vanno dichiarati manualmente nel progetto:namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)] public sealed class UnionAttribute : Attribute; public interface IUnion { object? Value { get; } } }
Una volta aggiunti questi tipi, si possono dichiarare e usare normalmente le union types. Il supporto IDE in Visual Studio sarà disponibile nella prossima build Insiders; il C# DevKit Insiders lo include già.Il quadro più ampio: la roadmap dell’esaustività
Le union types fanno parte di una strategia più ampia del team C# per portare la verifica dell’esaustività direttamente nel compilatore. Le proposte correlate attualmente in discussione sono:
- Closed hierarchies: il modificatore
closedsu una classe impedisce la dichiarazione di classi derivate al di fuori dell’assembly di definizione, consentendo al compilatore di considerare esaustive le espressioniswitchsulla gerarchia.- Closed enums: un
closedenum impedisce la creazione di valori diversi dai membri dichiarati, risolvendo il problema dei valori enum “numerici” inattesi.Insieme, questi tre meccanismi danno a C# un percorso completo verso la verifica statica dell’esaustività: union types per insiemi chiusi di tipi, closed hierarchies per gerarchie sigillate, closed enums per insiemi fissi di valori.
Conclusione
Le union types in C# 15 non sono un semplice porting delle discriminated union di F#: sono state progettate come aggiunta nativa all’ecosistema C#, composte da tipi esistenti, integrate con il sistema di pattern matching già consolidato, e compatibili con le librerie union-like già diffuse. La garanzia di esaustività del compilatore è il beneficio più concreto: i casi mancanti diventano avvisi a tempo di compilazione, non bug a runtime.La feature è in preview e il team accetta feedback attivamente su GitHub. Vale la pena esplorarla ora per contribuire alla forma definitiva della feature, prevista per la release di novembre 2026 con .NET 11.
Fonte: Explore union types in C# 15 – .NET Blog
Explore union types in C# 15 - .NET Blog
C# 15 introduces union types — declare a closed set of case types with implicit conversions and exhaustive pattern matching. Try unions in preview today and see the broader exhaustiveness roadmap.Bill Wagner (.NET Blog)
reshared this
The Pirate Post, The Privacy Post, Cybersecurity & cyberwarfare, Poliversity - Università ricerca e giornalismo e Elezioni e Politica 2026 reshared this.


Dave Wilburn
in reply to Catalin Cimpanu • • •Catalin Cimpanu
in reply to Dave Wilburn • • •@DaveMWilburn I legit think they don't know who this group operates under
you usually see informal attributions in private Slack channels and subreddits... but not for this one
Dave Wilburn
in reply to Catalin Cimpanu • • •Assuming this goes to trial, I wonder if a judge is going to tell them to "put up or shut up", and order prosecutors to refrain from implying any connections to hostile foreign intelligence services in front of the jury if they can't/won't provide evidence.
The criminal complaint only lists a single statute, Title 18 Section 371, which I believe is a fairly generic "conspiracy to defraud the United States" statute. The statute seems to require having two or more persons in the conspiracy, but my quick skim of the criminal complaint doesn't reveal any co-conspirators, indicted or otherwise. I believe the statute also requires the conspiracy be to commit an offense against the US Government or one of its agencies, but no specific offense is listed in the criminal complaint, and all of the listed victims appear to be private companies.
I can't tell if this is sloppy work or if the criminal complaint is just a placeholder pending a future indictment with greater detail, but it surprises me that we don't see all the elements of the crime satisfied in the criminal complaint. Maybe they'll add further evidence and further charges later relating to espionage and/or CFAA, or maybe they'll stick with this single charge.
I'm kinda curious what @SteveBellovin thinks of this.
fingfx.thomsonreuters.com/gfx/…
JJ
in reply to Catalin Cimpanu • • •Laundry Bear? Child stars always seem to fall in with a bad crowd. So sad.