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
structtöbbclassegyesí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?"
Fontos
A .NET 11 Preview 2-ben a futtatókörnyezet nem tartalmazza a felületet és IUnion a UnionAttribute felületet. Az egyesítő típusok használatához saját maga kell deklarálnia őket. A szükséges nyilatkozatok megtekintéséhez tekintse meg az uniós végrehajtást.
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:
public union OneOrMore<T>(T, IEnumerable<T>)
{
public IEnumerable<T> AsEnumerable() => Value switch
{
T single => [single],
IEnumerable<T> multiple => multiple,
_ => []
};
}
Uniós átalakítások
Az egyes esettípusoktól az egyesítő típusig implicit egyesítési átalakítás létezik. Nem kell explicit módon meghívnia egy konstruktort:
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. A konvertálási prioritással kapcsolatos részletekért tekintse meg a nyelvi specifikáció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",
};
}
Unió egyeztetése
Ha a minta egyezik az egyesítő típussal, a minták 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 két minta kivételt képez: a var minta és az elvetési _ minta az egyesítő értékre vonatkozik, nem pedig a tulajdonságára Value . Az egyesítő érték Pet?GetPet() rögzítésére használható var 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. Az alábbi minta azt ellenőrzi, hogy a Pet? nem null érték és az értéke Value nem null-e:
GetPet() switch
{
var pet and not null => ..., // 'var pet' captures the Pet?; 'not null' checks 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. Nincs szükség elvetési mintára (_) vagy var mintára, hogy megfeleljen bármilyen típusnak:
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
}
Null lehetőség
A fordító az alábbi szabályokkal követi nyomon az unió tulajdonságának Value null állapotát:
- 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, megvalósítja 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.
Ha eltérő viselkedésre van szüksége – például osztályalapú unióra, egyéni tárolási stratégiára, interop-támogatásra vagy meglévő típus módosítására – manuálisan is létrehozhat egy egyesítő típust.
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.
Minden szakszervezeti tagnak nyilvánosnak kell lennie. 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.
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üloutadja visszaboolés kézbesíti az értéket.
[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.
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 alábbi attribútum és felület támogatja az egyesítő típusokat fordításkor és futtatókörnyezetben:
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ó (public union Pet(Cat, Dog, Bird);) például Pet 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; }
}
Fontos
A .NET 11 Preview 2-ben ezek a típusok nem szerepelnek a futtatókörnyezetben. Az egyesítő típusok használatához deklarálnia kell őket a projektben. Ezek szerepelni fognak egy jövőbeli .NET-előzetesben.
C# nyelvspecifikáció
További információ: Unions feature specification.