Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Typ unii reprezentuje wartość, która może być jednym z kilku typów przypadków. Związki zapewniają niejawne konwersje z każdego typu przypadku, wyczerpujące dopasowanie wzorca i ulepszone śledzenie wartości null. Użyj słowa kluczowego union , aby zadeklarować typ unii:
public union Pet(Cat, Dog, Bird);
Ta deklaracja tworzy unię z trzema Pet typami przypadków: Cat, Dog, i Bird. Możesz przypisać dowolną wartość typu przypadku do zmiennej Pet . Kompilator zapewnia, że switch wyrażenia obejmują wszystkie typy przypadków.
Dokumentacja języka C# zawiera ostatnio wydaną wersję języka C#. Zawiera również początkową dokumentację dla funkcjonalności w publicznych wersjach testowych nadchodzącego wydania języka.
Dokumentacja identyfikuje dowolną funkcję po raz pierwszy wprowadzoną w ostatnich trzech wersjach języka lub w bieżącej publicznej wersji zapoznawczej.
Wskazówka
Aby dowiedzieć się, kiedy funkcja została po raz pierwszy wprowadzona w języku C#, zapoznaj się z artykułem dotyczącym historii wersji języka C#.
Zadeklaruj unię, gdy wartość musi być dokładnie jednym ze stałych zestawów typów i chcesz, aby kompilator wymuszał obsługę każdej możliwości. Typowe scenariusze obejmują:
-
Zwraca wynik lub błąd: metoda zwraca wartość powodzenia lub wartość błędu, a obiekt wywołujący musi obsłużyć obie te wartości. Związek taki jak
union Result(Success, Error)sprawia, że zestaw wyników jest jawny. -
Wysyłanie komunikatów lub poleceń: system przetwarza zamknięty zestaw typów komunikatów. Związek zapewnia, że nowe typy komunikatów generują ostrzeżenia w czasie kompilacji na każdym
switch, który jeszcze ich nie obsługuje. - Zastępowanie interfejsów znaczników lub abstrakcyjnych klas bazowych: jeśli używasz interfejsu lub klasy abstrakcyjnej wyłącznie do grupowania typów do dopasowywania wzorców, unia zapewnia wyczerpujące sprawdzanie bez konieczności dziedziczenia lub współużytkowanych elementów członkowskich.
Związek różni się od innych deklaracji typów w ważny sposób:
- W przeciwieństwie do elementu
classlubstruct, unia nie definiuje nowych składowych danych. Zamiast tego komponuje istniejące typy do zamkniętego zestawu alternatyw. -
interfaceW przeciwieństwie do elementu , unia jest zamknięta — należy zdefiniować pełną listę typów przypadków w deklaracji, a kompilator używa tej listy do sprawdzania wyczerpującości. -
recordW przeciwieństwie do elementu , unia nie dodaje równości, klonowania ani dekonstrukcji zachowania. Związek koncentruje się na "którym przypadku jest?" zamiast "jakie pola ma?"
Typy unii opierają się na typach UnionAttribute i IUnion w System.Runtime.CompilerServices przestrzeni nazw. Środowisko uruchomieniowe .NET 11 obejmuje te typy.
Deklaracje unii
Deklaracja unii określa nazwę i listę typów przypadków:
public union Pet(Cat, Dog, Bird);
Typy przypadków mogą być dowolnym typem, który konwertuje na object, w tym klasy, struktury, interfejsy, parametry typu, typy dopuszczające wartość null i inne związki. W poniższych przykładach przedstawiono różne możliwości typów przypadków:
public record class Cat(string Name);
public record class Dog(string Name, string Breed);
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);
Gdy typ przypadku jest typem wartości (na przykład int), deklaracja unii pola wartości i zapisuje ją jako pojedyncze object? odwołanie.
Niestandardowe typy unii mogą używać innej strategii magazynowania.
Deklaracja unii może zawierać treść z dodatkowymi członkami, podobnie jak struktura, z zastrzeżeniem pewnych ograniczeń. Deklaracje unii nie mogą zawierać pól wystąpień, właściwości automatycznych ani zdarzeń podobnych do pól. Nie można również zadeklarować publicznych konstruktorów za pomocą jednego parametru, ponieważ kompilator generuje te konstruktory jako składowe tworzenia unii. Każdy zadeklarowany konstruktor musi użyć inicjatora this(...) , aby delegować bezpośrednio lub pośrednio do wygenerowanego konstruktora. Poniższa Length unia dodaje TotalMeters właściwość, która używa dopasowania wzorca do obsługi każdego typu przypadku wraz z metodą łączącą Add dwie długości:
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);
}
Konwersje unii
Niejawna konwersja unii istnieje z każdego typu przypadku do typu unii:
static void BasicConversion()
{
Pet pet = new Dog("Rex", "Labrador");
Console.WriteLine(pet.Value); // output: Dog { Name = Rex, Breed = Labrador }
Pet pet2 = new Cat("Whiskers");
Console.WriteLine(pet2.Value); // output: Cat { Name = Whiskers }
}
Konwersje unii działają przez wywołanie odpowiedniego wygenerowanego konstruktora. Jeśli operator konwersji niejawnej zdefiniowanej przez użytkownika istnieje dla tego samego typu, operator zdefiniowany przez użytkownika ma pierwszeństwo przed konwersją unii. Jeśli do wartości źródłowej ma zastosowanie więcej niż jeden typ przypadku, konwersja unii jest niejednoznaczna, a kompilator zgłasza błąd. Aby uzyskać szczegółowe informacje na temat priorytetu konwersji, zobacz specyfikację funkcji.
Konwersja unii do struktury unii dopuszczanej do wartości null (T?) działa również wtedy, gdy T jest typem unii:
static void NullableUnionExample()
{
Pet? maybePet = new Dog("Buddy", "Beagle");
Pet? noPet = null;
Console.WriteLine(Describe(maybePet)); // output: 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",
};
}
Dopasowywanie wzorca unii
Jeśli używasz wzorca typu lub deklaracji w wyrażeniu unii, wzorzec testuje zawartość unii, a nie wystąpienie unii. Na przykład wzorzec sprawdza, Dog czy Value element Dogto :
static void PatternMatching()
{
Pet pet = new Dog("Rex", "Labrador");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
Formularz wzorca określa, która wartość testuje:
| Wzór | Zachowanie w wyrażeniu unii |
|---|---|
Wzorzec typu lub deklaracji, taki jak Dog lub Dog dog |
Sprawdza zawartość określonego typu. |
Wzorzec właściwości lub pozycyjny z typem, takim jak Dog { Breed: "Labrador" } |
Sprawdza zawartość względem typu, a następnie stosuje zagnieżdżone wzorce do tej wartości przypadku. |
Wzorzec właściwości lub pozycyjny bez typu, na przykład { Name: "Rex" } |
Używa właściwości lub Deconstruct metody wystąpienia unii. |
| Wzorzec listy | Używa składowych liczby i indeksowania wystąpienia unii, a nie elementów członkowskich jego zawartości. |
var lub odrzuć _ wzorzec |
Przechwytuje lub dopasowuje wystąpienie unii przychodzącej bez jego rozpasywania. |
| Wzorzec stały lub relacyjny | Testuje zawartość. Wzorzec null obsługuje również odwołanie do unii null lub otokę dopuszczaną do wartości null. |
Na przykład zdefiniuj Pet unię z właściwością tylko Name do odczytu, która deleguje do zawartego Cat elementu lub Dog. Wzorce typów w wyrażeniu właściwości switch przetestuj zawartość:
public union Pet(Cat, Dog)
{
public string Name => this switch
{
Cat cat => cat.Name,
Dog dog => dog.Name,
null => "",
};
}
Typowany wzorzec Dog { Breed: "Labrador" } sprawdza zawartość i odczytuje właściwość psa Breed, która nie ma związku. Nietypowy wzorzec { Name: "Rex" } odczytuje właściwość unii Name , więc pasuje również do kota o nazwie Rex:
Pet pet = new Dog("Rex", "Labrador");
Console.WriteLine(pet is Dog { Breed: "Labrador" }); // output: True
Console.WriteLine(pet is { Name: "Rex" }); // output: True
pet = new Cat("Rex");
Console.WriteLine(pet is Dog { Breed: "Labrador" }); // output: False
Console.WriteLine(pet is { Name: "Rex" }); // output: True
Wzorzec not neguje zagnieżdżony wzorzec. W pet is not nullsystemie wzorzec zagnieżdżony null sprawdza zawartość, więc not null jest to przydatny test wartości. Natomiast pusty nietypowy wzorzec { }właściwości sprawdza wystąpienie unii. Zawsze pasuje do nienaliwalnej unii struktury, nawet jeśli Value ma wartość null:
Pet empty = default;
Console.WriteLine(empty is { }); // output: True
Console.WriteLine(empty is not null); // output: False
if (empty is var captured)
{
Console.WriteLine(captured.Value is null); // output: True
}
Wzorce logiczne zachowują te różnice. W var pet and not nullprogramie var pet przechwytuje unię przychodzącą i not null sprawdza, czy ma on zawartość inną niż null. W not null and var petsystemie var wzorzec nadal przechwytuje unię przychodzącą: not nie zmienia wartości przekazanej do wzorca po prawej stronie. Natomiast w Dog and { Breed: "Labrador" }systemie wzorzec typu przekazuje dopasowanie Dog do wzorca właściwości.
Uwaga / Notatka
Wzorzec typu musi być zgodny z co najmniej jednym typem przypadku.
Pet W przypadku deklaracji w tym artykule pet is Pet tworzy błąd kompilatora CS8121: żaden z ich typów przypadków nie może być zgodny Petz . Nie jest to test środowiska uruchomieniowego wystąpienia unii. Aby sprawdzić zawartość, użyj polecenia pet is not null lub pet.Value is not null, aby przechwycić sam związek, użyj var wzorca.
Krótko mówiąc, wzorce typów i deklaracji testuje zawartość. Wzorce rekursywne z typem testuje też zawartość; wzorce rekursywne bez typu używają własnych składowych unii. Te reguły są takie same w wyrażeniu is i wyrażeniu switch .
Dopasowywanie wartości null
W przypadku związków struktur wzorzec sprawdza, null czy Value ma wartość null:
static void NullHandling()
{
Pet pet = default;
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
Console.WriteLine(pet.Value is null); // output: True
}
W przypadku związków opartych na klasach następuje powodzenie, null gdy odwołanie do unii ma wartość null lub jego Value właściwość ma wartość 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 */ }
W przypadku typów struktury unii dopuszczanej do wartości null (Pet?) następuje powodzenie, null gdy otoka dopuszczana do wartości null lub gdy bazowej unii Value ma wartość null.
Wyczerpującość unii
Wyrażenie switch jest wyczerpujące w przypadku typów przypadków unii, gdy obsługuje każdy typ sprawy. Jeśli analiza wartości null uwzględnia również zawartość inną niż null, nie potrzebujesz odrzucenia (_) ani var ramienia:
static void PatternMatching()
{
Pet pet = new Dog("Rex", "Labrador");
var name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Console.WriteLine(name); // output: Rex
}
Aby obsłużyć zawartość null, preferuj null ramię na ramieniu var lub odrzuć (_). Wzorzec null określa intencję i zachowuje ostrzeżenia podczas dodawania nowego typu przypadku bez pasującego ramienia.
var Ramię lub odrzucanie akceptuje również każdy nowy przypadek, więc kompilator nie może ostrzec o tym braku przypadku.
Gdy śledzony stan Value null to "może wartość null", kompilator ostrzega również, jeśli pominięto null ramię. Na przykład NullablePet ma typ przypadku dopuszczalnego wartości null, więc jego zawartość parametru może mieć wartość null:
public union NullablePet(Cat?, Dog, Bird);
static string DescribeNullableCase(NullablePet pet) => pet switch
{
Cat cat => cat.Name,
Dog dog => dog.Name,
Bird bird => bird.Name,
null => "no pet",
};
Usunięcie ramienia z tego przełącznika null powoduje ostrzeżenie CS8655.
Analiza dopuszczana do wartości null nie gwarantuje, że wygenerowana unia ma zawartość inną niż null w czasie wykonywania. Domyślna unia zawiera wartość null, w tym w przypadku, gdy jest to domyślne pole lub element tablicy. Jeśli wszystkie typy przypadków nie są możliwe, domyślny śledzony stan Value jest jednak "nie null", więc kompilator nie musi ostrzegać o pominiętym null ramieniu. Obsługa null poprawności za każdym razem, gdy zawartość domyślna może dotrzeć do przełącznika:
static void NullHandling()
{
Pet pet = default;
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
Console.WriteLine(pet.Value is null); // output: True
}
W tym Pet przykładzie usunięcie null ramienia nie generuje ostrzeżenia missing-null-arm, chyba że wcześniej kod zmieni śledzony stan null, ale przełącznik zgłasza SwitchExpressionException , gdy otrzyma wartość domyślną. Wcześniejszy is null test może zmienić śledzony stan i spowodować ostrzeżenie. To zachowanie jest oddzielone od określonego przypisania: kompilator odrzuca odczyt nieprzypisanej zmiennej lokalnej.
Możliwość przypisania wartości null
Kompilator śledzi stan null właściwości unii Value za pomocą następujących reguł:
- Domyślny stan null właściwości unii
Valueto "może null", jeśli domyślnym stanem null każdego typu przypadku jest "może null". W przeciwnym razie domyślny stan null to "nie null". - Podczas tworzenia wartości unii na podstawie typu przypadku (za pomocą konstruktora lub konwersji unii)
Valuepobiera stan null wartości przychodzącej. - Gdy wzorzec
HasValuedostępu innego niż boxing lubTryGetValue(...)elementy członkowskie wysyłają zapytanie do zawartości unii, stanValuenull staje się "nie null" wtruegałęzi.
Niestandardowe typy związków
Kompilator konwertuje deklarację union na deklarację struct . Struktura jest oznaczona atrybutem [System.Runtime.CompilerServices.Union] i implementuje IUnion interfejs. Zawiera on publiczny konstruktor dla każdego typu przypadku i Value właściwość. Język udostępnia niejawne konwersje unii, które nazywają te konstruktory. Ta wygenerowana forma jest uważana. Zawsze jest to struktura, zawsze pola przypadków typu wartości i zawsze przechowuje zawartość jako object?.
Może być konieczne inne zachowanie, jeśli chcesz dostosować istniejący typ, utworzyć unię opartą na klasach lub użyć niestandardowej strategii magazynowania lub jeśli potrzebujesz obsługi międzyoperacyjnej. Typ unii można utworzyć ręcznie.
Każda klasa lub struktura z atrybutem jest [Union], jeśli jest zgodny ze wzorcem podstawowym unii. Podstawowy wzorzec unii wymaga:
- Atrybut
[Union]typu. - Co najmniej jeden publiczny konstruktor, z których każdy ma jedną wartość
inlub parametr. Typ parametru każdego konstruktora definiuje typ przypadku. - Właściwość publiczna
Valuetypuobject?(lubobject) z akcesoremget.
Wszyscy poprzedni członkowie związku muszą być publiczne. Kompilator używa tych elementów członkowskich do implementowania konwersji unii, dopasowywania wzorców i sprawdzania kompletności. Można również zaimplementować wzorzec dostępu bez boxingu lub utworzyć typ unii opartej na klasach. Niestandardowy typ unii może dodać więcej elementów członkowskich.
Kompilator zakłada, że niestandardowe typy unii spełniają następujące reguły zachowania:
-
Dźwięk:
Valuezawsze zwracanulllub wartość jednego z typów przypadków, w tym dla wartości domyślnej struktury. Unia niestandardowa może reprezentować jego wartość domyślną jako przypadek inny niż null, w przeciwieństwie do zawartości null domyślnej wygenerowanej unii. -
Stabilność: jeśli tworzysz wartość unii na podstawie typu sprawy,
Valuepasuje do tego typu przypadku (lub jest tonull, czy dane wejściowe tonull). - Równoważność tworzenia: jeśli wartość jest niejawnie konwertowana na dwa różne typy przypadków, oba elementy członkowskie tworzenia tworzą to samo zauważalne zachowanie.
-
Spójność wzorca dostępu:
HasValueelementy członkowskie iTryGetValue, jeśli są obecne, zachowują się równoważnie do bezpośredniego sprawdzaniaValue.
W poniższym przykładzie pokazano niestandardowy typ unii:
[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
}
Wzorzec dostępu bez boksu
Niestandardowy typ unii może opcjonalnie zaimplementować wzorzec dostępu bez użycia pola, aby umożliwić silnie typizowane przypadki typu wartości bez boksowania podczas dopasowywania wzorca. Ten wzorzec wymaga:
- Właściwość
HasValuetypubool, która zwracatruewartość , gdyValuenienulljest . -
TryGetValueMetoda dla każdego typu przypadku, który zwracabooli dostarcza wartość za pośrednictwem parametruout.TryGetValueZwracatruewartość tylko wtedy, gdyValuejest wartością inną niż null tego typu przypadku.outTyp parametru to identity-cabrio do typu przypadku lub do bazowego typu wartości, gdy typ przypadku jest typem wartości dopuszczanej wartości 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
}
W przypadku dopasowywania wzorca kompilator wywołuje TryGetValue kontrole wzorców typów (na union is Tprzykład ) i HasValue kontrole wzorców wartości null (na przykład union is null), unikając tworzenia pól dla przypadków typu wartości. Każdy element członkowski ma zastosowanie do własnego rodzaju wzorca — żadna z nich nie jest rezerwą dla drugiego. Gdy brakuje elementu członkowskiego, kompilator sprawdza Value właściwość dla tego wzorca. Aby uzyskać więcej informacji, zobacz How the compiler generates code for pattern matching (Jak kompilator generuje kod na potrzeby dopasowywania wzorca).
Dostawcy będący członkami unii
Typ unii może delegować swoje składowe unii do publicznego interfejsu zagnieżdżonego IUnionMembers , który zawiera bezpośrednio i implementuje. Ten interfejs jest dostawcą składowych unii. Kompilator znajduje elementy członkowskie unii tylko w interfejsie dostawcy, w tym dziedziczone elementy członkowskie. Członkowie zadeklarowani tylko dla samego typu unii nie są używane jako członkowie unii.
W przypadku korzystania z dostawcy składowych unii interfejs musi zadeklarować następujące wymagane elementy członkowskie:
-
Metody statyczne
Create: jeden dla każdego typu przypadku z pojedynczym parametrem i zwracanym typem identity-cabrio do typu unii. Te metody określają typy przypadków. -
Valuewłaściwość: publicznaobjectlubobject?właściwość z akcesorem, który zwraca zawartągetwartość.
Opcjonalnie można zadeklarować te elementy członkowskie w interfejsie, aby kompilator wygenerował bardziej wydajny kod dopasowania wzorca:
-
TryGetValuemetody: jeden dla każdego typu przypadku zwracanyboolz parametremouttypu przypadku. -
HasValuewłaściwość: właściwość publicznaboolz akcesoremgetzwracanymtrue, gdyValuenienulljest .
Sposób generowania przez kompilator kodu do dopasowywania wzorców: TryGetValue i HasValue stosowania do różnych rodzajów wzorców — żadna z nich nie jest rezerwą dla innych:
- W przypadku wzorca typu, takiego jak
union is T, kompilator wywołujeTryGetValue(out T value)po zadeklarowaniu, wyodrębniając wartość i stosując do niego wzorzec bez boksowania. W przeciwnym razie kompilator stosuje wzorzec doValuewłaściwości, która wykonuje sprawdzanieTtypu środowiska uruchomieniowego i może zaznaczyć wartość, gdyTjest typem wartości. - W przypadku wzorca o wartości null, takiego jak
union is null, kompilator wywołujeHasValuepolecenie po zadeklarowaniu, aby sprawdzić, czy unia zawiera wartość. W przeciwnym razie kompilator stosuje wzorzec null bezpośrednio doValuewłaściwości .
Każdy element członkowski jest niezależnie opcjonalny: bez TryGetValue, wzorce typów używają Value; bez HasValuewzorców wartości null należy użyć .Value Deklarowanie dowolnego elementu członkowskiego umożliwia kompilatorowi generowanie bardziej wydajnego, silnie typizowanego kodu dla tego rodzaju wzorca:
[System.Runtime.CompilerServices.Union]
public struct 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; }
// Optional but recommended: TryGetValue enables efficient pattern matching
bool TryGetValue(out T value);
bool TryGetValue(out Exception value);
}
object? IUnionMembers.Value => _value;
public bool TryGetValue(out T value)
{
if (_value is T t)
{
value = t;
return true;
}
value = default!;
return false;
}
public bool TryGetValue(out Exception value)
{
if (_value is Exception e)
{
value = e;
return true;
}
value = default!;
return false;
}
}
Dostawcy składowych unii są przydatni, gdy typ unii wymaga konstruktora prywatnego lub gdy logika tworzenia wymaga wzorca fabryki, takiego jak typy record class unii.
Typy unii opartej na klasach
Klasa może być również typem unii. Ten typ unii jest przydatny, gdy potrzebujesz semantyki odwołań lub dziedziczenia:
[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",
};
}
W przypadku związków opartych na klasach null wzorzec pasuje zarówno do odwołania o wartości null, jak i wartości null Value.
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz specyfikację funkcji Unii .