Union-typer
En union-typ är en typ som alltid är exakt ett av ett känt antal alternativ. Kompilatorn vet alla möjliga alternativ och kan kontrollera att du hanterat dem alla i en switch.
När du läst detta ska du kunna
- Deklarera en union-typ med
unionochcase - Använda mönstermatchning mot en union-typ
- Förklara varför union-typer är bättre än arv för den här typen av modellering
- Läsa kompilatorfel när ett alternativ saknas
Syntax
public union Result{ case Success(string Data); case Error(int Code, string Message);}Varje case är ett alternativ. En Result är antingen Success eller Error — aldrig något annat, aldrig null.
Skapa och använda
Result ok = new Result.Success("Sparad!");Result fail = new Result.Error(404, "Hittades inte");Mönstermatchning
void PrintResult(Result result){ switch (result) { case Result.Success(var data): Console.WriteLine($"OK: {data}"); break; case Result.Error(var code, var message): Console.WriteLine($"Fel {code}: {message}"); break; }}Kompilatorn kontrollerar att alla case är täckta. Om du lägger till ett nytt alternativ i unionen och glömmer att hantera det i en switch — kompileringsfel.
Praktiskt exempel — HTTP-svar
public union HttpResult{ case Ok(string Body); case NotFound(string Url); case ServerError(int StatusCode, string Detail); case Unauthorized();}HttpResult Fetch(string url){ if (url.StartsWith("https://")) return new HttpResult.Ok("<html>...</html>");
return new HttpResult.NotFound(url);}
var result = Fetch("http://exempel.se");
string message = result switch{ HttpResult.Ok(var body) => $"Svar: {body[..20]}", HttpResult.NotFound(var url) => $"Sidan saknas: {url}", HttpResult.ServerError(var code, var msg) => $"Serverfel {code}: {msg}", HttpResult.Unauthorized() => "Logga in först"};
Console.WriteLine(message);Output
Sidan saknas: http://exempel.seJämförelse med arv
Samma modellering med arv kräver mer kod och kompilatorn kan inte kontrollera att alla fall är täckta:
// Med arv — kompilatorn kan inte garantera uttömlighetabstract class Result { }class Success(string Data) : Result;class Error(int Code, string Message) : Result;
// switch behöver ett default-fallResult r = GetResult();string msg = r switch{ Success s => s.Data, Error e => e.Message, _ => throw new Exception("Okänd typ") // nödvändigt men meningslöst};Med union-typer är _-fallet onödigt och kompilatorn flaggar det om du ändå skriver det.
case utan parametrar
En case kan vara tom — ett rent tillstånd utan data:
public union LoginState{ case LoggedOut(); case LoggingIn(); case LoggedIn(string Username, string Token); case SessionExpired(string Username);}TL;DR
// Deklarationpublic union Shape{ case Circle(double Radius); case Rectangle(double Width, double Height); case Triangle(double Base, double Height);}
// Användning med exhaustive matchingdouble Area(Shape shape) => shape switch{ Shape.Circle(var r) => Math.PI * r * r, Shape.Rectangle(var w, var h) => w * h, Shape.Triangle(var b, var h) => b * h / 2};Union-typer passar bäst när ett värde alltid är ett av ett känt antal alternativ och du vill ha kompilatorgarantier att alla alternativ hanteras.