Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Typ sjednocení představuje hodnotu, která může být jedním z několika typů případů. Sjednocení poskytují implicitní převody z každého typu případu, vyčerpávající porovnávání vzorů a sledování rozšířené hodnoty nullability. Pomocí klíčového union slova deklarujte typ sjednocení:
public union Pet(Cat, Dog, Bird);
Tato deklarace vytvoří sjednocení se třemi Pet typy případů: Cat, Doga Bird. Proměnné můžete přiřadit libovolnou Pet hodnotu typu případu. Kompilátor zajišťuje, že switch výrazy pokrývají všechny typy případů.
Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.
Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.
Návod
Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.
Deklarujte sjednocení, pokud hodnota musí být přesně jednou z pevných množiny typů a chcete, aby kompilátor vynucoval, aby se každá možnost zpracovala. Mezi běžné scénáře patří:
-
Vrátí výsledek nebo chybu: Metoda vrátí hodnotu úspěchu nebo chybovou hodnotu a volající musí zpracovat obojí. Sjednocením je
union Result(Success, Error)sada výsledků explicitní. -
Odesílání zpráv nebo příkazů: Systém zpracovává uzavřenou sadu typů zpráv. Sjednocení zajišťuje, že nové typy zpráv vytvářejí upozornění v době kompilace v každém
switch, který je ještě nezpracuje. - Nahrazení rozhraní značek nebo abstraktních základních tříd: Pokud používáte rozhraní nebo abstraktní třídu výhradně k seskupení typů pro porovnávání vzorů, sjednocení poskytuje kontrolu úplnosti bez nutnosti dědičnosti nebo sdílených členů.
Sjednocení se liší od jiných deklarací typů důležitými způsoby:
-
classNa rozdíl od nebostructnedefinuje sjednocení nové datové členy. Místo toho vytvoří existující typy do uzavřené sady alternativ. -
interfaceNa rozdíl od sjednocování je uzavřeno – definujete úplný seznam typů případů v deklaraci a kompilátor použije tento seznam k kontrolám úplnosti. -
recordNa rozdíl od sjednocení nepřidává chování rovnosti, klonování ani dekonstrukce. Sjednocení se zaměřuje na "jaký případ je?" a ne na to, jaká pole mají?
Deklarace sjednocení
Deklarace sjednocení určuje název a seznam typů případů:
public union Pet(Cat, Dog, Bird);
Typy případů mohou být libovolný typ, který se převede na objecttřídy, struktury, rozhraní, parametry typu, typy s možnou hodnotou null a další sjednocení. Následující příklady ukazují různé možnosti typů případů:
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public record class None;
public record class Some<T>(T Value);
public union Option<T>(None, Some<T>);
public union IntOrString(int, string);
Pokud je typem případu typ hodnoty (například int), hodnota se zadává do pole, když je uložena Value ve vlastnosti sjednocení. Sjednocení ukládají jejich obsah jako jediný object? odkaz.
Deklarace sjednocení může obsahovat tělo s dalšími členy, stejně jako struktura, s výhradou některých omezení. Deklarace sjednocení nemůžou obsahovat pole instancí, automatické vlastnosti ani události podobné polím. Nemůžete také deklarovat veřejné konstruktory s jedním parametrem, protože kompilátor tyto konstruktory generuje jako členy vytváření sjednocení. Následující Length sjednocení přidá TotalMeters vlastnost, která používá porovnávání vzorů pro zpracování každého typu případu spolu s metodou Add , která kombinuje dvě délky:
public record class Meters(double Value);
public record class Feet(double Value);
public union Length(Meters, Feet)
{
public double TotalMeters => this switch
{
Meters m => m.Value,
Feet f => f.Value * 0.3048,
_ => throw new InvalidOperationException("The Length has no value."),
};
public Length Add(Length other) => new Meters(TotalMeters + other.TotalMeters);
}
Sjednocení převodů
Implicitní sjednocovacího převodu existuje z každého typu případu na typ sjednocení:
static void BasicConversion()
{
Pet pet = new Dog("Rex");
Console.WriteLine(pet.Value); // output: Dog { Name = Rex }
Pet pet2 = new Cat("Whiskers");
Console.WriteLine(pet2.Value); // output: Cat { Name = Whiskers }
}
Sjednocovací převody fungují voláním odpovídajícího generovaného konstruktoru. Pokud existuje operátor implicitního převodu definovaný uživatelem pro stejný typ, má operátor definovaný uživatelem přednost před sjednocovacího převodu. Pokud je pro zdrojovou hodnotu stejně použitelný více než jeden typ případu, je sjednocování převodu nejednoznačný a kompilátor hlásí chybu. Podrobnosti o prioritě převodu najdete ve specifikaci funkce.
Převod sjednocení na strukturu sjednocení s možnou hodnotou null (T?) funguje také v případech, kdy T je sjednocovací typ:
static void NullableUnionExample()
{
Pet? maybePet = new Dog("Buddy");
Pet? noPet = null;
Console.WriteLine(Describe(maybePet)); // output: Dog: Buddy
Console.WriteLine(Describe(noPet)); // output: no pet
static string Describe(Pet? pet) => pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
}
Porovnávání vzorů sjednocení
Při porovnávání u typu sjednocení se vzory obecně vztahují na vlastnost sjednocení Value , nikoli na samotnou hodnotu sjednocení. Toto chování při rozbalování znamená, že sjednocení je transparentní pro porovnávání vzorů:
static void PatternMatching()
{
Pet pet = new Dog("Rex");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
Tři vzory jsou výjimky tohoto pravidla: vzor zahození, vzor a not vzor se vztahují na samotnou sjednocovat _ hodnotu, nikoli její Valuevar vlastnost. Slouží var k zachycení sjednocovacího hodnoty při GetPet() vrácení hodnoty Pet? (Nullable<Pet>):
if (GetPet() is var pet) { /* pet is the Pet? value returned from GetPet */ }
V logických vzorech se každá větev řídí pravidlem rozbalování jednotlivě. Levá and větev vzoru může změnit příchozí hodnotu, kterou vidí pravá větev. Vzhledem k tomu, že se not vzor vztahuje na příchozí sjednocující hodnotu, nikoli na její Valuehodnotu , not null úvodní hodnota nezabalí hodnotu pro větev, která ji následuje:
GetPet() switch
{
// 'var pet' captures the Pet?; 'not null' applies to the Pet? value (not pet.Value)
var pet and not null => ...,
// 'not null' doesn't unwrap to Pet, so 'var value' still captures the Pet?
not null and var value => ...,
}
Poznámka:
Vzhledem k tomu, že vzory platí pro Value, vzor jako pet is Pet obvykle neodpovídá, protože Pet se testuje s obsahem sjednocení, nikoli samotné sjednocení.
Porovnávání s hodnotou Null
U sjednocení struktur vzor kontroluje, null jestli Value má hodnotu null:
static void NullHandling()
{
Pet pet = default;
Console.WriteLine(pet.Value is null); // output: True
var description = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
Console.WriteLine(description); // output: no pet
}
U sjednocení založených na třídách je úspěšné, null pokud je odkaz na sjednocení samotný null nebo jeho Value vlastnost má hodnotu null:
Result<string>? result = null;
if (result is null) { /* true — the reference is null */ }
Result<string> empty = new Result<string>((string?)null);
if (empty is null) { /* true — Value is null */ }
U typů struktury sjednocení s možnou hodnotou null (Pet?) proběhne úspěšně, null pokud obálka s možnou hodnotou null nemá žádnou hodnotu nebo pokud je podkladová sjednocení Value null.
Úplnost sjednocení
Výraz switch je vyčerpávající, když zpracovává všechny typy případů sjednocení. Kompilátor varuje pouze v případě, že typ případu není zpracován. Pokud je výraz rozhodně přiřazený, nemusíte zahrnout vzor zahození (_) ani var vzor odpovídající žádnému typu:
static void PatternMatching()
{
Pet pet = new Dog("Rex");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
Pokud je stav null vlastnosti sjednocení Value "možná null", musíte také zpracovat null , aby se zabránilo upozornění:
static void NullHandling()
{
Pet pet = default;
Console.WriteLine(pet.Value is null); // output: True
var description = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "no pet",
};
Console.WriteLine(description); // output: no pet
}
Tato situace může nastat, když union je výraz výchozí hodnotou nebo není rozhodně přiřazený, jak je znázorněno v předchozí ukázce.
Nullovatelnost
Kompilátor sleduje stav null vlastnosti sjednocení Value pomocí následujících pravidel:
- Výchozí stav null vlastnosti sjednocení
Valueje "možná null", pokud výchozí stav null jakéhokoli typu případu je "možná null". Jinak výchozí stav null není null. - Když vytvoříte sjednocovací hodnotu z typu případu (prostřednictvím konstruktoru nebo sjednocovacího převodu),
Valuezíská stav null příchozí hodnoty. - Při dotazování obsahu sjednocovacího modelu
HasValueneboTryGetValue(...)členů modelu přístupu se stavValuenull ve větvi změní natruehodnotu "not null".
Vlastní typy sjednocení
Kompilátor převede union deklaraci na struct deklaraci. Struktura je označena atributem [System.Runtime.CompilerServices.Union] a implementuje IUnion rozhraní. Zahrnuje veřejný konstruktor a implicitní převod pro každý typ případu spolu s Value vlastností. Tento vygenerovaný formulář je názorný. Vždy se jedná o strukturu, vždy pole hodnotových případů a vždy ukládá obsah jako object?.
Pokud chcete přizpůsobit existující typ, vytvořit sjednocení založené na třídách nebo použít vlastní strategii úložiště nebo pokud potřebujete podporu spolupráce, může být potřeba jiné chování. Sjednocovacího typu můžete vytvořit ručně.
Jakákoli třída nebo struktura s atributem [Union] je typ sjednocení , pokud se řídí základním vzorem sjednocení. Základní sjednocovacího vzoru vyžaduje:
- Atribut
[Union]typu. - Jeden nebo více veřejných konstruktorů, z nichž každý má jednu hodnotu nebo
inparametr. Typ parametru každého konstruktoru definuje typ případu. - Veřejná
Valuevlastnost typuobject?(neboobject) s příslušenstvímget.
Všechny předchozí členy odborů musí být veřejné. Kompilátor tyto členy používá k implementaci sjednocovacího převodu, porovnávání vzorů a kontrol úplnosti. Můžete také implementovat vzor přístupu bez rámečku nebo vytvořit typ sjednocení založené na třídách. Váš vlastní typ sjednocení může přidat další členy.
Kompilátor předpokládá, že vlastní typy sjednocení vyhovují těmto pravidlům chování:
-
Zvuk:
Valuevždy vrátínullhodnotu nebo hodnotu jednoho z typů případů – nikdy hodnotu jiného typu. Pro sjednocení strukturdefaultvytvoří zValuenull. -
Stabilita: Pokud vytvoříte sjednocovat hodnotu z typu případu,
Valueodpovídá tomuto typu případu (nebo je-linullvstupnull). - Ekvivalence vytvoření: Pokud je hodnota implicitně konvertibilní na dva různé typy případů, oba členy vytváření vytvářejí stejné pozorovatelné chování.
-
Konzistence vzorů přístupu: Členové
HasValueaTryGetValuečlenové se chovají stejně jako při kontroleValuepřímo.
Následující příklad ukazuje vlastní typ sjednocení:
[System.Runtime.CompilerServices.Union]
public struct Shape : System.Runtime.CompilerServices.IUnion
{
private readonly object? _value;
public Shape(Circle value) { _value = value; }
public Shape(Rectangle value) { _value = value; }
public object? Value => _value;
}
public record class Circle(double Radius);
public record class Rectangle(double Width, double Height);
static void ManualUnionExample()
{
Shape shape = new Shape(new Circle(5.0));
var area = shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle r => r.Width * r.Height,
};
Console.WriteLine($"{area:F2}"); // output: 78.54
}
Vzor přístupu bez boxování
Vlastní sjednocovací typ může volitelně implementovat vzor přístupu bez boxingu , který umožňuje přístup silného typu k případům typu hodnota bez boxování během porovnávání vzorů. Tento model vyžaduje:
- Vlastnost
HasValuetypubool, která vracítrue, kdyžValuenenínull. - Metoda
TryGetValuepro každý typ případu, který vrátíboola doručí hodnotu prostřednictvím parametruout.TryGetValuevrátítruepouze v případě, žeValueje hodnota jiného typu než null daného typu případu. Typoutparametru je konvertibilní na typ případu nebo na typ podkladové hodnoty, pokud je typ případu typu hodnoty null.
[System.Runtime.CompilerServices.Union]
public struct IntOrBool : System.Runtime.CompilerServices.IUnion
{
private readonly int _intValue;
private readonly bool _boolValue;
private readonly byte _tag; // 0 = none, 1 = int, 2 = bool
public IntOrBool(int? value)
{
if (value.HasValue)
{
_intValue = value.Value;
_tag = 1;
}
}
public IntOrBool(bool? value)
{
if (value.HasValue)
{
_boolValue = value.Value;
_tag = 2;
}
}
public object? Value => _tag switch
{
1 => _intValue,
2 => _boolValue,
_ => null
};
public bool HasValue => _tag != 0;
public bool TryGetValue(out int value)
{
value = _intValue;
return _tag == 1;
}
public bool TryGetValue(out bool value)
{
value = _boolValue;
return _tag == 2;
}
}
static void NonBoxingExample()
{
IntOrBool val = new IntOrBool((int?)42);
var description = val switch
{
int i => $"int: {i}",
bool b => $"bool: {b}",
};
Console.WriteLine(description); // output: int: 42
}
Kompilátor dává přednost TryGetValue před Value vlastností při implementaci porovnávání vzorů, což zabraňuje krabicové typy hodnot.
Poskytovatelé členů Unie
Typ sjednocení může členy sjednocení delegovat na vnořené IUnionMembers rozhraní. Pokud je toto rozhraní přítomno, kompilátor hledá Create metody továrny místo konstruktorů:
[System.Runtime.CompilerServices.Union]
public record class Outcome<T> : Outcome<T>.IUnionMembers
{
private readonly object? _value;
private Outcome(object? value) => _value = value;
public interface IUnionMembers
{
static Outcome<T> Create(T? value) => new(value);
static Outcome<T> Create(Exception? value) => new(value);
object? Value { get; }
}
object? IUnionMembers.Value => _value;
}
Poskytovatelé členů unie jsou užiteční, když typ sjednocení potřebuje privátní konstruktor nebo když logika vytváření vyžaduje vzor továrny, například s record class typy sjednocení.
Typy sjednocení založené na třídách
Třída může být také sjednocovacího typu. Tento typ sjednocení je užitečný v případě, že potřebujete referenční sémantiku nebo dědičnost:
[System.Runtime.CompilerServices.Union]
public class Result<T> : System.Runtime.CompilerServices.IUnion
{
private readonly object? _value;
public Result(T? value) { _value = value; }
public Result(Exception? value) { _value = value; }
public object? Value => _value;
}
static void ClassUnionExample()
{
Result<string> ok = new Result<string>("success");
Result<string> err = new Result<string>(new InvalidOperationException("failed"));
Console.WriteLine(Describe(ok)); // output: OK: success
Console.WriteLine(Describe(err)); // output: Error: failed
static string Describe(Result<string> result) => result switch
{
string s => $"OK: {s}",
Exception e => $"Error: {e.Message}",
null => "null",
};
}
U sjednocení založených na třídách null vzor odpovídá odkazu null i hodnotě null Value.
Implementace Unie
Typy sjednocení se spoléhají na typy UnionAttributeIUnion v System.Runtime.CompilerServices oboru názvů. Modul runtime zahrnuje tyto typy počínaje .NET 11 Preview 5:
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public sealed class UnionAttribute : Attribute;
public interface IUnion
{
object? Value { get; }
}
Deklarace Sjednocení generované kompilátorem implementují IUnion. Libovolnou hodnotu sjednocení můžete zkontrolovat za běhu pomocí IUnion:
if (value is IUnion { Value: null }) { /* the union's value is null */ }
Když deklarujete union typ, kompilátor vygeneruje strukturu, která implementuje IUnion. Deklarace (Pet) se například public union Pet(Cat, Dog, Bird); stane ekvivalentem:
[Union] public struct Pet : IUnion
{
public Pet(Cat value) => Value = value;
public Pet(Dog value) => Value = value;
public Pet(Bird value) => Value = value;
public object? Value { get; }
}
specifikace jazyka C#
Další informace najdete ve specifikaci funkcí Sjednocení .
Viz také
- Systém typů jazyka C#
- Porovnávání vzorů
- Výraz Switch
- typy hodnot
- Záznamy