Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az egyesítő típus olyan értéket jelöl, amely több esettípus egyike lehet. Az egyes esettípusok implicit konverzióit, a teljes mintaegyezést és a továbbfejlesztett nullability trackingt biztosítják.
union A kulcsszóval deklarálhat egy egyesítési típust:
public union Pet(Cat, Dog, Bird);
Ez a deklaráció három esettípussal hoz létre egy Pet egyesítést: Cat, Dogés Bird. Bármilyen esettípus-értéket hozzárendelhet egy Pet változóhoz. A fordító biztosítja, hogy a switch kifejezések minden esettípusra kiterjednek.
A C# nyelv referenciadokumentuma a C# nyelv legújabb kiadású verzióját ismerteti. Emellett a közelgő nyelvi kiadás nyilvános előzetes verziójú funkcióinak kezdeti dokumentációját is tartalmazza.
A dokumentáció azonosítja azokat a funkciókat, amelyeket először a nyelv utolsó három verziójában vagy az aktuális nyilvános előzetes verziókban vezetnek be.
Jótanács
Ha meg szeretné tudni, hogy mikor jelent meg először egy funkció a C#-ban, tekintse meg a C# nyelvi verzióelőzményeiről szóló cikket.
Deklaráljon egy egyesítést, ha egy értéknek pontosan egy rögzített típuskészletnek kell lennie, és azt szeretné, hogy a fordító kényszerítse minden lehetőség kezelését. Gyakori forgatókönyvek a következők:
-
Eredmény vagy hiba: A metódus sikerértéket vagy hibaértéket ad vissza, a hívónak mindkettőt kezelnie kell. Egy ilyen
union Result(Success, Error)egyesítés explicitvé teszi az eredmények halmazát. -
Üzenet- vagy parancsküldés: A rendszer egy zárt üzenettípus-készletet dolgoz fel. Az egyesítők biztosítják, hogy az új üzenettípusok fordítási idejű figyelmeztetéseket készítsenek minden
switcholyan üzenetnél, amely még nem kezeli őket. - Jelölőillesztők vagy absztrakt alaposztályok cseréje: Ha egy felületi vagy absztrakt osztályt kizárólag a mintaegyeztetés típusainak csoportosítására használ, a egyesítés teljes körű ellenőrzést biztosít öröklés vagy megosztott tagok megkövetelése nélkül.
Az egyesítők fontos módon különböznek a többi típusdeklarációtól:
- Egy vagy
classtöbbstructegyesítéssel ellentétben az egyesítők nem határoznak meg új adattagokat. Ehelyett a meglévő típusokat egy zárt alternatívahalmazba állítja össze. -
interfaceA egyesítéstől eltérően a deklarációban meg kell határoznia az esettípusok teljes listáját, és a fordító ezt a listát használja a teljesség ellenőrzéséhez. -
recordA egyesítéssel ellentétben a egyesítés nem ad egyenlőséget, klónozást vagy dekonstruálási viselkedést. Az egyesítők inkább a "melyik esetet" helyezik előtérbe, nem pedig a "milyen mezőkkel rendelkeznek?"
Uniós nyilatkozatok
Az egyesítő deklarációk az esettípusok nevét és listáját határozzák meg:
public union Pet(Cat, Dog, Bird);
Az esettípusok bármilyen típusúak lehetnek, amelyek osztályokat object, szerkezeteket, interfészeket, típusparamétereket, null értékű típusokat és más egyesítőket is átalakítanak. Az alábbi példák különböző esettípus-lehetőségeket mutatnak be:
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);
Ha egy esettípus értéktípus (például int), akkor az érték akkor lesz bekeretítve, ha az egyesítő Value tulajdonságában van tárolva. A szakszervezetek egyetlen object? hivatkozásként tárolják a tartalmukat.
Az egyesítő nyilatkozatok tartalmazhatnak olyan testületet is, amely további tagokkal rendelkezik, csakúgy, mint egy struktúra, bizonyos korlátozások mellett. Az egyesítő deklarációk nem tartalmazhatnak példánymezőket, automatikus tulajdonságokat vagy mezőszerű eseményeket. A nyilvános konstruktorok egyetlen paraméterrel sem deklarálhatók, mert a fordító ezeket a konstruktorokat egyesítő létrehozási tagokként hozza létre. Az alábbi Length összevonás egy olyan tulajdonságot TotalMeters ad hozzá, amely mintaegyeztetéssel kezeli az összes esettípust, valamint egy Add olyan metódust, amely két hosszt egyesít:
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);
}
Uniós átalakítások
Az implicit egyesítő átalakítás minden esettípustól az egyesítő típusig létezik:
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 }
}
Az egyesítő átalakítások a megfelelő generált konstruktor meghívásával működnek. Ha egy felhasználó által definiált implicit konverziós operátor létezik ugyanahhoz a típushoz, a felhasználó által definiált operátor elsőbbséget élvez az egyesítő átalakítással szemben. Ha egynél több esettípus egyaránt alkalmazható a forrásértékre, az egyesítő átalakítás nem egyértelmű, és a fordító hibát jelez. A konverziós prioritással kapcsolatos részletekért tekintse meg a szolgáltatás specifikációját.
A null értékű egyesítési szerkezetre (T?) való egyesítés akkor is működik, ha T egy egyesítési típus:
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",
};
}
Egyesítő minta egyeztetése
Ha a minta egyezik az egyesítő típussal, a minták általában az unió tulajdonságára Value vonatkoznak, nem magára az egyesítő értékre. Ez a "feloldás" viselkedés azt jelenti, hogy az egyesítés transzparens a mintaegyezéshez:
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
}
A szabály alól három minta kivételt képez: az elvetési _ minta, a var minta és a not minta az egyesítő értékre vonatkozik, nem pedig a tulajdonságára Value . Az egyesítő érték varGetPet() rögzítésére használható Pet? a (Nullable<Pet>):
if (GetPet() is var pet) { /* pet is the Pet? value returned from GetPet */ }
A logikai mintákban minden ág egyenként követi a kibontási szabályt. A minta bal ága módosíthatja and a jobb ág által látott bejövő értéket. Mivel a minta a not bejövő egyesítő értékre vonatkozik ahelyett, hogy annak Value, a bevezető not null nem bontja ki az azt követő ág értékét:
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 => ...,
}
Megjegyzés:
Mivel a minták vonatkoznak rá Value, az olyan minták, mint pet is Pet általában nem egyeznek, mivel Pet az unió tartalmával van tesztelve, és nem magát az uniót.
Null egyezés
A struct unions esetében a null minta ellenőrzi, hogy null-e Value :
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
}
Osztályalapú egyesítések esetén akkor sikeres, null ha az egyesítő hivatkozás maga null, vagy a tulajdonsága Value 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 */ }
A null értékű egyesítőszerkezet-típusok (Pet?) null akkor sikeresek, ha a null értékű burkolónak nincs értéke, vagy ha a mögöttes egyesítés Value null értékű.
Az unió teljessége
A switch kifejezések teljes körűek, ha az összes egyesítő esettípust kezeli. A fordító csak akkor figyelmeztet, ha a kis- és nagybetűket nem kezeli. Nem kell olyan elvetési mintát (_) vagy var mintát felvennie, amely bármilyen típusnak megfelel, ha a kifejezés egyértelműen hozzárendelve van:
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
}
Ha az unió Value tulajdonságának null állapota "talán null", akkor a figyelmeztetés elkerülése érdekében a következőt kell kezelnie 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
}
Ez a helyzet akkor fordulhat elő, ha a union kifejezés az alapértelmezett érték, vagy nincs egyértelműen hozzárendelve, ahogy az előző példában is látható.
Null lehetőség
A fordító az alábbi szabályokkal követi nyomon az unió tulajdonságának Value null állapotát:
- Az egyesítő
Valuetulajdonság alapértelmezett null állapota "talán null", ha bármilyen esetben az alapértelmezett null állapot "talán null". Ellenkező esetben az alapértelmezett null állapot "nem null". - Ha egy esettípusból (konstruktoron vagy egyesítési átalakításon keresztül) hoz létre egyesítő értéket,
Valuea bejövő érték null állapotát kapja meg. - Amikor a nem boxing hozzáférési minta
HasValuevagyTryGetValue(...)tagok lekérdezik az unió tartalmát, az ág null állapotaValue"nem null"truelesz.
Egyéni egyesítő típusok
A fordító deklarációvá union alakítja a deklarációt struct . A szerkezet az attribútummal [System.Runtime.CompilerServices.Union] van megjelölve, és implementálja az interfészt IUnion . Tartalmaz egy nyilvános konstruktort és egy implicit konverziót minden esettípushoz egy Value tulajdonsággal együtt. A létrehozott űrlap véleményezhető. Ez mindig egy struktúra, mindig érték típusú eseteket ad meg, és a tartalmat object?mindig a következőképpen tárolja.
Eltérő viselkedésre lehet szükség, ha egy meglévő típust szeretne átalakítani, osztályalapú uniót szeretne létrehozni, vagy egyéni tárolási stratégiát szeretne használni, vagy ha interop-támogatásra van szüksége. Az egyesítő típusokat manuálisan is létrehozhatja.
Bármely attribútummal rendelkező [Union] osztály vagy struktúra egyesítő típus , ha az alapszintű egyesítési mintát követi. Az alapszintű egyesítő minta a következőt igényli:
- Egy
[Union]attribútum a típuson. - Egy vagy több nyilvános konstruktor, amelyek mindegyike egyetlen értékkel vagy
inparaméterrel rendelkezik. Az egyes konstruktorok paramétertípusa egy esettípust határoz meg. - Olyan köztulajdon
Value, amelynek típusaobject?(vagyobject) tartozékkal van eltulajdonítvaget.
Az előző szakszervezeti tagoknak nyilvánosnak kell lenniük. A fordító ezeket a tagokat használja az egyesítő átalakítások, a minták egyeztetése és a teljesség ellenőrzése érdekében. Implementálhatja a nem boxing hozzáférési mintát is, vagy létrehozhat egy osztályalapú uniótípust. Az egyéni egyesítő típus további tagokat adhat hozzá.
A fordító feltételezi, hogy az egyéni egyesítő típusok megfelelnek az alábbi viselkedési szabályoknak:
-
Hang:
Valuemindig az egyik esettípus értékét adja visszanull– soha ne adjon meg más típusú értéket. A struct unions esetébendefaultaValuenull. -
Stabilitás: Ha egyesítő értéket hoz létre egy kis- és nagybetűtípusból,
Valueakkor az adott esettípusnak megfelelő (vagynullha a bemenet voltnull). - Létrehozási egyenértékűség: Ha egy érték implicit módon két különböző esettípusra konvertálható, mindkét létrehozási tag ugyanazt a megfigyelhető viselkedést hozza létre.
-
Hozzáférési minta konzisztenciája: A
HasValueTryGetValuetagok és ha vannak, ugyanúgy viselkednek, mint a közvetlen ellenőrzésValue.
Az alábbi példa egy egyéni egyesítő típust mutat be:
[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
}
Nem boxing hozzáférési minta
Az egyéni egyesítési típusok opcionálisan implementálhatják a nem dobozoló hozzáférési mintát , hogy az érték típusú esetekhez való erős hozzáférést lehetővé tegyék anélkül, hogy a mintaegyezés során boxolást hajtanak végre. Ehhez a mintához a következők szükségesek:
- Olyan
HasValuetípusúbooltulajdonság, amely akkor adtruevissza, haValuenemnull. - Egy
TryGetValuemetódus minden egyes esettípushoz, amely egy paraméteren keresztülbooladja visszaoutés kézbesíti az értéket.TryGetValuecsak akkor adtruevissza, haValueaz adott esettípus nem null értékű. Aoutparaméter típusa identitás-átalakítható a kis- és nagybetűk típusára, illetve az alapul szolgáló értéktípusra, ha a kis- és nagybetűk értéke null értékű.
[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
}
A fordító előnyben részesíti a tulajdonságot TryGetValue a Value mintaegyeztetés megvalósításakor, ami elkerüli az értéktípusok bevitelét.
Uniós tagszolgáltatók
Az egyesítő típusok egymásba ágyazott felületre delegálhatják a szakszervezet tagjait IUnionMembers . Ha ez a felület jelen van, a fordító a konstruktorok helyett gyári metódusokat keres Create :
[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;
}
Az uniótag-szolgáltatók akkor hasznosak, ha az egyesítő típushoz magánkonstruktorra van szükség, vagy ha a létrehozási logikához gyári minta szükséges, például egyesítő típusok esetén record class .
Osztályalapú egyesítő típusok
Az osztály lehet egyesítő típus is. Az ilyen típusú egyesítés akkor hasznos, ha referencia szemantikára vagy öröklésre van szüksége:
[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",
};
}
Az osztályalapú egyesítések esetében a null minta egy null hivatkozással és null értékkel Valueis megegyezik.
Uniós végrehajtás
Az egyesítő típusok a UnionAttribute névtérben lévő és IUnion a System.Runtime.CompilerServices típusokra támaszkodnak. A futtatókörnyezet az alábbi típusokat tartalmazza a .NET 11 5. előzetes verziótól kezdve:
namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
public sealed class UnionAttribute : Attribute;
public interface IUnion
{
object? Value { get; }
}
A fordító IUnionáltal létrehozott uniós deklarációk. Az egyesítő értékeket futásidőben a következővel IUnionellenőrizheti:
if (value is IUnion { Value: null }) { /* the union's value is null */ }
Amikor deklarál egy típust union , a fordító létrehoz egy szerkezetet, amely megvalósítja azokat IUnion. A deklaráció (Pet) például public union Pet(Cat, Dog, Bird); a következőnek felel meg:
[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; }
}
C# nyelvspecifikáció
További információ: Unions feature specification.