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.
Poznámka:
Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.
Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v příslušných poznámkách ze schůzky jazykového návrhu (LDM).
Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .
Problém šampiona: https://github.com/dotnet/csharplang/issues/9662
Shrnutí
Sjednocení je sada vzájemně propojených funkcí, které kombinují podporu typů sjednocení jazyka C#:
-
Typy sjednocení: Struktury a třídy, které mají
[Union]atribut, jsou rozpoznány jako typy sjednocení a podporují chování sjednocení. - Typy případů: Typy sjednocení mají sadu typů případů, které jsou dány parametry konstruktorům a metodám továrny.
-
Chování sjednocení: Typy sjednocení podporují následující chování sjednocení:
- Převody sjednocení: Existují implicitní sjednocovat převody z každého typu případu na typ sjednocení.
- Porovnávání sjednocení: Porovnávání vzorů s hodnotami sjednocení implicitně "rozbalí" jejich obsah a místo toho použije vzor na podkladovou hodnotu.
- Úplnost sjednocení: Výrazy přepínače nad hodnotami sjednocení jsou vyčerpávající, pokud byly porovnány všechny typy případů, aniž by bylo nutné použít náhradní případ.
- Sjednocovací možnost nullability: Analýza nulové hodnoty má vylepšené sledování stavu null obsahu sjednocení.
- Vzory sjednocení: Všechny typy sjednocení se řídí základním vzorem sjednocení, existují ale další volitelné vzory pro konkrétní scénáře.
- Deklarace sjednocení: Syntaxe zkratky umožňuje přímo deklarovat typy sjednocení. Implementace je "opinionated" - deklarace struktury, která se řídí základním vzorem sjednocení a ukládá obsah jako jedno referenční pole.
- Rozhraní unie: Jazyk zná několik rozhraní a používá se při jeho implementaci deklarací sjednocení.
Motivation
Sjednocení jsou dlouho požadovaná funkce jazyka C#, která umožňuje vyjádřit hodnoty z uzavřené sady typů způsobem, kterému porovnávání vzorů může důvěřovat, aby bylo vyčerpávající.
Oddělení typů sjednocení adeklarací sjednocení umožňuje jazyku C# mít stručnou syntaxi deklarace sjednocení se sémantikou se stanoviskem a zároveň umožňuje, aby existující typy nebo typy s jinými možnostmi implementace mohly vyjádřit výslovný souhlas s chováním sjednocení.
Navrhované sjednocení v jazyce C# jsou sjednocení typů , nikoli "diskriminované" nebo "označené". "Diskriminované sjednocení" lze vyjádřit pomocí deklarací typu jako typů. Alternativně je možné je implementovat jako uzavřenou hierarchii, což je další související, nadcházející funkce jazyka C# zaměřená na vyčerpávající funkčnost.
Podrobný návrh
Typy sjednocení
Jakýkoli typ třídy nebo struktury s atributem System.Runtime.CompilerServices.UnionAttribute je považován za sjednocující typ:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(Class | Struct, AllowMultiple = false)]
public class UnionAttribute : Attribute;
}
Typ sjednocení musí dodržovat určitý vzor členů veřejné sjednocení, který musí být deklarován u samotného typu sjednocení nebo delegován na "poskytovatele členů sjednocení".
Někteří členové odborů jsou povinní a jiné jsou volitelné.
Typ sjednocení má sadu typů případů , které jsou vytvořeny na základě podpisů určitých členů sjednocení.
K obsahu sjednocované hodnoty lze přistupovat prostřednictvím Value vlastnosti. Jazyk předpokládá, že Value pouze někdy obsahuje hodnotu jednoho z typů případů nebo null (viz Well-formness).
Poskytovatelé členů Unie
Ve výchozím nastavení se členové sjednocení nacházejí v samotném typu sjednocení. Pokud však typ sjednocení přímo obsahuje deklaraci rozhraní volaného IUnionMembers , rozhraní funguje jako zprostředkovatel člena sjednocení. V takovém případě jsou členové sjednocení nalezeni pouze u poskytovatele členů unie, nikoli u samotného typu sjednocení.
Rozhraní zprostředkovatele člena sjednocení musí být veřejné a samotný typ sjednocení ho musí implementovat jako rozhraní.
Používáme termín sjednocení definující typ pro typ, ve kterém jsou nalezeni členové sjednocení: Poskytovatel členů sjednocení, pokud existuje, a samotný typ sjednocení.
Členové unie
Členové sjednocení se vyhledávají podle názvu a podpisu u typu definovaného sjednocením. Nemusí být deklarovány přímo u typu definující sjednocení, ale mohou být zděděné.
Jedná se o chybu, že žádný člen unie není veřejný.
Členy vytvoření a Value vlastnost jsou povinné a souhrnně se označují jako základní vzor sjednocení.
Členové HasValue a TryGetValue členové se souhrnně označují jako vzor přístupu sjednocení bez boxingu.
Různí členové sjednocení jsou popsáni v následujícím článku.
Členové vytváření sjednocení
Členové vytváření sjednocení se používají k vytvoření nových hodnot sjednocení z hodnoty typu případu.
Pokud je sjednocovací typ samotný typ sjednocení, každý konstruktor s jedním parametrem je konstruktor sjednocení. Typy případů sjednocení jsou identifikovány jako sada typů sestavených z typů parametrů těchto konstruktorů následujícím způsobem:
- Pokud je typ parametru typu s možnou hodnotou null (bez ohledu na hodnotu nebo odkaz), je typ případu základním typem.
- V opačném případě je typ případu typem parametru.
// Union constructor making `Dog` a case type
public Pet(Dog value) { ... }
// Union constructor making `int` a case type
public Union(int? value) { ... }
// Union constructor making `string` a case type
public Union(string? value) { ... }
Pokud je sjednocovací typ zprostředkovatelem člena sjednocení, každá statická Create metoda s jedním parametrem a návratovým typem, který je konvertibilní na samotný typ sjednocení, je metoda objektu pro vytváření sjednocení.
Typy případů sjednocení jsou identifikovány jako sada typů sestavených z typů parametrů těchto metod továrny následujícím způsobem:
- Pokud je typ parametru typu s možnou hodnotou null (bez ohledu na hodnotu nebo odkaz), je typ případu základním typem.
- V opačném případě je typ případu typem parametru.
// Union factory method making `Cat` a case type
public static Pet Create(Cat value) { ... }
// Union factory method making `int` a case type
public static Union Create(int? value) { ... }
// Union factory method making `string` a case type
public static Union Create(string? value) { ... }
Konstruktory sjednocení a metody sjednocovací továrny se souhrnně označují jako členy vytváření sjednocení.
Jediný parametr člena vytvoření sjednocení musí být podle hodnoty nebo in parametru.
Typ sjednocení musí mít alespoň jeden člen vytvoření sjednocení, a proto alespoň jeden typ případu.
Value – vlastnost
Vlastnost Value umožňuje přístup k hodnotě obsažené ve sjednocení bez ohledu na jeho typ případu.
Každý sjednocující typ musí deklarovat Value vlastnost typu object? nebo object. Vlastnost musí mít přístupové objekty get a může mít volitelně přístup initset nebo přístup, který může být jakýkoliv přístupnost a není používán kompilátorem.
// Union 'Value' property
public object? Value { get; }
Ne boxing access members
Sjednocovací typ se může rozhodnout pro dodatečnou implementaci modelu přístupu sjednocení, který umožňuje podmíněný přístup silného typu, a také způsob, jak zkontrolovat hodnotu null.
Kompilátor tak může efektivněji implementovat porovnávání vzorů, pokud jsou typy případů typy hodnot a uloženy v rámci sjednocení.
Ne boxovací přístupové členy jsou:
- Vlastnost
HasValuetypubools veřejnýmgetpříslušenstvím. Volitelně může mít přístup neboinitsetpřístup, který může být jakýkoliv přístup a kompilátor ho nepoužívá. - Metoda
TryGetValuepro každý typ případu. Metoda vrátíboola vezme jeden out-parametr typu, který je identity-převoditelný na typ případu.
// Non-boxing access members
public bool HasValue { get { ... } }
public bool TryGetValue(out Dog value) { ... }
HasValue je očekáváno, že vrátí hodnotu true, pokud a pouze v případě, že sjednocovat Value není null.
TryGetValue je očekáváno, že vrátí hodnotu true, pokud a pouze pokud je sjednocení Value daného typu případu, a pokud ano, doručte tuto hodnotu v parametru out metody.
Dobrá strukturovanost
Jazyk a kompilátor tvoří řadu předpokladů chování o sjednocovaných typech. Pokud se typ kvalifikuje jako typ sjednocení, ale nesplňuje tyto předpoklady, nemusí chování sjednocení fungovat podle očekávání.
-
Zvuk: Vlastnost
Valuese vždy vyhodnotí jako null nebo na hodnotu typu případu. To platí i pro výchozí hodnotu typu sjednocení. -
Stabilita: Pokud se z typu případu vytvoří sjednocovaná hodnota,
Valuevlastnost se shoduje s tímto typem případu nebo hodnotou null. Pokud je hodnota sjednocení vytvořena znullhodnoty,Valuevlastnost budenull. - Ekvivalence vytvoření: Pokud je hodnota implicitně konvertibilní na dva různé typy případů, člen vytvoření pro některý z těchto typů případů má stejné pozorovatelné chování při zavolání s danou hodnotou.
-
Konzistence vzorů přístupu: Chování
HasValueaTryGetValuene boxování přístupových členů, pokud jsou přítomny, je pozorovatelně ekvivalentní tomu, že kontrola vlastnostiValuepřímo.
Příklady typů sjednocení
Pet implementuje základní sjednocovacího modelu u samotného typu sjednocení:
[Union] public record struct Pet
{
// Creation members = case types are 'Dog' and 'Cat'
public Pet(Dog value) => Value = value;
public Pet(Cat value) => Value = value;
// 'Value' property
public object? Value { get; }
}
IntOrBool implementuje model přístupu bez rámečku na samotném typu sjednocení:
public record struct IntOrBool
{
private bool _isBool;
private int _value;
public IntOrBool(int value) => (_isBool, _value) = (false, value);
public IntOrBool(bool value) => (_isBool, _value) = (true, value ? 1 : 0);
public object Value => _isBool ? _value is 1 : _value;
public bool HasValue => true;
public bool TryGetValue(out int value)
{
value = _value;
return !_isBool;
}
public bool TryGetValue(out bool value)
{
value = _isBool && _value is 1;
return _isBool;
}
}
Poznámka: Toto je jen příklad toho, jak je možné implementovat model přístupu, který není boxovací. Uživatelský kód může obsah uložit libovolným způsobem. Konkrétně nezabrání implementaci boxování! Název non-boxing odkazuje na povolení implementace porovnávání vzorů kompilátoru pro přístup ke každému typu případu silným typem, na rozdíl od object?vlastnosti -typed Value .
Result<T> implementuje základní vzor prostřednictvím zprostředkovatele člena sjednocení:
public record class Result<T> : Result<T>.IUnionMembers
{
object? _value;
public interface IUnionMembers
{
public static Result<T> Create(T value) => new() { _value = value };
public static Result<T> Create(Exception value) => new() { _value = value };
public object? Value { get; }
}
object? IUnionMembers.Value => _value;
}
Chování sjednocení
Chování sjednocení se obecně implementuje pomocí základního sjednocovacího vzoru. Pokud sjednocení nabízí model přístupu bez krabicového přístupu, párování vzorů sjednocení bude přednostně využito.
Sjednocení převodů
Sjednocovacího převodu se implicitně převede na typ sjednocení z každého z jeho typů případů. Konkrétně existuje převod sjednocení na typ sjednocení z typu U nebo výrazu E , pokud existuje standardní implicitní převod z E typu C a C je typ parametru člena vytvořeníUsjednocení .
Pokud je sjednocovacího typu U struktura, existuje převod sjednocení na typ U? z typu nebo výrazu E , pokud existuje standardní implicitní převod z E typu C a C je typ parametru člena vytvořeníUsjednocení .
Sjednocovacího převodu není sám o sobě standardní implicitní převod. Proto se nesmí účastnit implicitního převodu definovaného uživatelem ani jiného sjednocovacího převodu.
Neexistují žádné explicitní převody sjednocení nad rámec implicitních sjednocovacího převodu. A to i v případě, že existuje explicitní převod z E typu Cpřípadu sjednocení , to neznamená, že existuje explicitní převod z E daného typu sjednocení.
Převod sjednocení se provede voláním člena vytvoření sjednocení:
Pet pet = dog;
// becomes
Pet pet = new Pet(dog);
// and
Result<string> result = "Hello"
//becomes
Result<string> result = Result<string>.IUnionMembers.Create("Hello");
Jedná se o chybu, pokud řešení přetížení nenajde jednoho nejlepšího kandidáta člena nebo pokud tento člen není jedním ze členů sjednocení typu sjednocení.
Sjednocování převodu je jen další "forma" implicitního převodu definovaného uživatelem. Použitelný uživatelem definovaný operátor převodu "stíny" sjednocení převodu.
Odůvodnění tohoto rozhodnutí:
Pokud někdo napsal uživatelem definovaný operátor, měl by získat prioritu. Jinými slovy, pokud uživatel ve skutečnosti napsal vlastní operátor, chce, abychom ho volali. Stávající typy s operátory převodu transformované na sjednocovací typy nadále fungují stejným způsobem s ohledem na stávající kód využívající operátory dnes.
V následujícím příkladu má implicitní převod definovaný uživatelem přednost před sjednocovacího převodu.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => ...
public S1(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static implicit operator S1(int x) => ...
}
class Program
{
static S1 Test1() => 10; // implicit operator S1(int x) is used
static S1 Test2() => (S1)20; // implicit operator S1(int x) is used
}
V následujícím příkladu při použití explicitního přetypování v kódu má explicitní uživatelem definovaný převod přednost před sjednocovací převod. Pokud ale kód neobsahuje explicitní přetypování, použije se sjednocovací převod, protože explicitní převod definovaný uživatelem není použitelný.
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => ...
public S2(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static explicit operator S2(int x) => ...
}
class Program
{
static S2 Test3() => 10; // Union conversion S2.S2(int) is used
static S2 Test4() => (S2)20; // explicit operator S2(int x)
}
Sjednocování
Pokud je příchozí hodnota vzoru typu sjednocení nebo typu sjednocení s možnou hodnotou null, může být v závislosti na vzoru hodnota null a obsah podkladové sjednocovací hodnoty "unwrapped".
Pro nepodmíněné _ vzory a var vzory se použije na samotnou příchozí hodnotu. Například:
if (GetPet() is var pet) { ... } // 'pet' is the union value returned from `GetPet`
Všechny ostatní vzory se ale implicitně aplikují na vlastnost podkladové Value sjednocení:
if (GetPet() is Dog dog) { ... } // 'Dog dog' is applied to 'GetPet().Value'
if (GetPet() is null) { ... } // 'null' is applied to 'GetPet().Value'
if (GetPet() is { } value) { ... } // '{ } value' is applied to 'GetPet().Value'
U logických vzorů se toto pravidlo použije jednotlivě na větve s ohledem na to, že levá větev and vzoru může ovlivnit příchozí typ pravé větve:
GetPet() switch
{
var pet and not null => ... // 'var pet' applies to the incoming 'Pet' and 'not null' to its 'Value'
not null and var value => ... // 'not null' applies to the 'Value' as does 'var value' because of the
// left branch changing the incoming type to `object?`.
}
Poznámka: Toto pravidlo znamená, že GetPet() is Pet pet pravděpodobně nebude úspěšné, protože Pet se použije na obsah, ne na Pet samotné sjednocení.
Poznámka: Důvodem odlišného zacházení s nepodmíněným var vzorem (stejně _jako , což je v podstatě zkratka) var _je předpoklad, že jejich použití se kvalitativní liší od jiných vzorů.
var vzory se používají jednoduše k pojmenování odpovídající hodnoty, často vnořených vzorech, například PetOwner{ Pet: var pet }. Zde je užitečné sémantika zachovat pet typ Petsjednocení , místo Value vlastnosti být dereferenced na neužitečný object? typ.
Pokud je příchozí hodnota typ třídy, null bude vzor úspěšný bez ohledu na to, jestli je samotná null sjednocující hodnota nebo její obsažená hodnota:null
if (result is null) { ... } // if (result == null || result.Value == null)
Jiné vzory sjednocování budou úspěšné pouze v případech, kdy samotná sjednocovat hodnota není null.
if (result is 1) { ... } // if (result != null && result.Value is 1)
Podobně platí, že pokud příchozí hodnota je typ hodnot nullable (zabalení sjednocovacího typu struktury), null bude vzor úspěšný bez ohledu na to, zda je null samotná příchozí hodnota nebo její obsažená hodnota:null
if (result is null) { ... } // if (result.HasValue == false || result.GetValueOrDefault().Value == null)
Jiné sjednocující vzory budou úspěšné pouze v případech, kdy samotná příchozí hodnota není null.
if (result is 1) { ... } // if (result.HasValue && result.GetValueOrDefault().Value is 1)
Kompilátor bude preferovat implementaci chování vzorů pomocí členů, které jsou předepsány vzorem přístupu bez boxování. I když je možné provést jakoukoli optimalizaci v mezích pravidel dobře vytvořeného formátu, platí minimální sada, která se má použít:
- Pro vzor, který naznačuje kontrolu konkrétního typu
T, pokudTryGetValue(S value)je metoda k dispozici, a existuje identita, nebo implicitní odkaz/ boxing převod zTS, pak se tato metoda použije k získání hodnoty. Vzor se pak použije na tuto hodnotu. Pokud existuje více než jedna taková metoda, pak jakýkoliv, kde převod zTdoSnení krabicový převod upřednostňovaný, pokud je k dispozici. Pokud existuje stále více než jedna metoda, je vybrána způsobem definovaným implementací. - V opačném případě pro vzor, který předpokládá kontrolu
null, pokudHasValueje vlastnost k dispozici, se tato vlastnost používá ke kontrole, zda je sjednocovat hodnota null. - V opačném případě se vzor použije na výsledek přístupu k
IUnion.Valuevlastnosti příchozí sjednocení.
Operátor is-type použitý u sjednocovacího typu má stejný význam jako vzor typu použitý u sjednocovacího typu.
Úplnost sjednocení
Typ sjednocení se předpokládá, že je "vyčerpán" jeho typy případů. To znamená, že výraz je vyčerpávající, switch pokud zpracovává všechny typy případů sjednocení:
var name = pet switch
{
Dog dog => ...,
Cat cat => ...,
// No warning about non-exhaustive switch
};
Nullovatelnost
Stav null vlastnosti sjednocení Value je sledován jako jakákoli jiná vlastnost s těmito úpravami:
- Při zavolání člena vytvoření sjednocení (explicitně nebo prostřednictvím převodu sjednocení) získá nová sjednocení
Valuestav null příchozí hodnoty. - Pokud vzor přístupu
HasValuebez rámečku neboTryGetValue(...)se používá k dotazování na obsah sjednocovacího typu (explicitně nebo prostřednictvím porovnávání vzorů), má vlivValuena stav nullability stejným způsobem, jako kdybyValuebyl zaškrtnutý přímo: StavValuenull ve větvi se změní natruehodnotu null.
I když je sjednocující přepínač jinak vyčerpávající, pokud je stav null vlastnosti příchozí sjednocení Value "možná null", zobrazí se upozornění na neošetřenou hodnotu null.
Pet pet = GetNullableDog(); // 'pet.Value' is "maybe null"
var value = pet switch
{
Dog dog => ...,
Cat cat => ...,
// Warning: 'null' not handled
}
Sjednocení rozhraní
Následující rozhraní používají jazyk v implementaci sjednocovacích funkcí.
Rozhraní pro přístup sjednocení
Rozhraní IUnion označuje typ jako typ sjednocení v době kompilace a poskytuje způsob, jak získat přístup k obsahu sjednocení za běhu.
public interface IUnion
{
// The value of the union or null
object? Value { get; }
}
Sjednocení vygenerovaná kompilátorem implementují toto rozhraní.
Příklad použití:
if (value is IUnion { Value: null }) { ... }
Deklarace sjednocení
Prohlášení Unie představují stručný a názorný způsob deklarování typů sjednocení v jazyce C#. Deklarují strukturu, která používá jeden odkaz na objekt pro uložení jeho Value, což znamená:
- Boxing: Všechny typy hodnot v jejich typech případů budou do pole pro zadání.
- Kompaktnost: Hodnoty sjednocení obsahují pouze jedno pole.
Záměrem je sjednocovat deklarace, které pokrývají velkou většinu případů použití poměrně pěkně. Očekává se, že dva hlavní důvody pro kódování konkrétních typů sjednocení namísto použití deklarací sjednocení:
- Přizpůsobení existujících typů vzorům sjednocení za účelem získání chování sjednocení
- Implementace jiné strategie úložiště, například z důvodů efektivity nebo spolupráce.
Syntaxe
Deklarace sjednocení má název a seznam typů konstruktorů sjednocení .
union_declaration
: attributes? struct_modifier* 'partial'? 'union' identifier type_parameter_list?
'(' type (',' type)* ')' struct_interfaces? type_parameter_constraints_clause*
(`{` struct_member_declaration* `}` | ';')
;
Kromě omezení členů struktury (§16.3) platí pro členy odborů následující:
- Pole instance, automatické vlastnosti nebo události podobné polím nejsou povoleny.
- Explicitně deklarované veřejné konstruktory s jedním parametrem nejsou povoleny.
- Explicitně deklarované konstruktory musí použít
this(...)inicializátor na (přímo nebo nepřímo) delegáta na jeden z vygenerovaných konstruktorů.
Typy sjednocovacích konstruktorů mohou být libovolný typ, který se převede na objectrozhraní, parametry typu, typy s možnou hodnotou null a další sjednocení. Výsledné případy se mohou překrývat a sjednocení vnořit nebo mít hodnotu null.
Příklady:
// Union of existing types
public union Pet(Cat, Dog, Bird);
// Union with function member
public union OneOrMore<T>(T, IEnumerable<T>)
{
public IEnumerable<T> AsEnumerable() => Value switch
{
IEnumerable<T> list => list,
T value => [value],
}
}
// "Discriminated" union with freshly declared case types
public record class None();
public record class Some<T>(T value);
public union Option<T>(None, Some<T>);
#### Lowering
A union declaration is lowered to a struct declaration with
* the same attributes, modifiers, name, type parameters and constraints,
* implicit implementations of `IUnion`,
* a `public object? Value { get; }` auto-property,
* a public constructor for each *union constructor* type,
* any members in the union declaration's body.
It is an error for user-declared members to conflict with generated members.
Example:
``` c#
public union Pet(Cat, Dog){ ... }
Je nižší na:
[Union] public struct Pet : IUnion
{
public Pet(Cat value) => Value = value;
public Pet(Dog value) => Value = value;
public object? Value { get; }
... // original body
}
Otevřené otázky
[Vyřešeno] Je deklarace sjednocení záznamem?
Deklarace sjednocení je nižší na strukturu záznamu.
Myslím, že toto výchozí chování je zbytečné a vzhledem k tomu, že není konfigurovatelné, výrazně omezí scénáře použití. Záznamy generují velké množství kódu, který se buď nepoužívá, nebo neodpovídá konkrétním požadavkům. Například záznamy jsou v základu kódu kompilátoru z důvodu tohoto bloudu kódu docela zakázané. Myslím, že by bylo lepší změnit výchozí:
- Ve výchozím nastavení deklarace sjednocení deklaruje běžnou strukturu s pouze členy specifické pro sjednocení.
- Uživatel může deklarovat sjednocení záznamů:
record union U(E1, ...) ...
Rozlišení: Deklarace sjednocení je prostý struktura, nikoli struktura záznamu. Nepodporuje se.record union ...
[Vyřešeno] Syntaxe deklarace sjednocení
Vypadá to, že navrhovaná syntaxe je neúplná nebo zbytečně omezuje. Vypadá to například, že základní klauzule není povolená. Nicméně, mohu si snadno představit, že je třeba implementovat rozhraní, například.
Myslím, že kromě seznamu typů prvků by syntaxe měla odpovídat běžné struct/record struct deklaraci, kde struct je klíčové slovo nahrazeno klíčovým slovem union .
Rozlišení: Omezení se odebere.
[Vyřešeno] Členové prohlášení Unie
Pole instance, automatické vlastnosti nebo události podobné polím nejsou povoleny.
To se cítí libovolně a naprosto nepotřebné.
Rozlišení: Omezení se uchovává.
[Vyřešeno] Typy hodnot s možnou hodnotou null jako typy případů Sjednocení
Typy případů sjednocení jsou identifikovány jako sada typů parametrů z těchto konstruktorů. Typy případů sjednocení jsou identifikovány jako sada typů parametrů z těchto metod továrny.
Současně:
Metoda
TryGetValuepro každý typ případu. Metoda vrátíboola vezme jeden out-parametr typu, který odpovídá danému typu případu následujícím způsobem:
- Pokud je typ případu typ hodnoty nullable, měl by být typ parametru konvertibilní identitou na základní typ.
- Jinak by měl být typ typu identity konvertibilní na typ případu.
Existuje mezi typy případů výhodu, že typ hodnoty null lze použít zejména v případě, že vzor typu nemůže jako cílový typ použít typ hodnoty nullable? Zdá se, že bychom mohli jednoduše říci, že pokud je typ parametru konstruktoru nebo továrny typ hodnoty nullable, pak odpovídající typ případu je podkladovým typem. Pak bychom pro metodu TryGetValue nemuseli potřebovat další klauzuli, všechny výstupní parametry jsou typy malých a malých písmen.
Rozlišení: Návrh je schválen.
[Vyřešeno] Výchozí stav vlastnosti s možnou Value hodnotou null
Pro sjednocovací typy, u kterých žádný z typů případů nemá hodnotu null, je výchozí stav
Value"not null" místo "možná null".
Při novém návrhu, kde Value vlastnost není definována v některém obecném rozhraní, ale je rozhraní API, které konkrétně patří do deklarovaného typu, pravidlo uvedené výše se cítí jako over-engineering. Kromě toho pravidlo pravděpodobně vynutí uživatele, aby používali typy s možnou hodnotou null v situacích, kdy by se jinak nepoužívaly typy s možnou hodnotou null.
Představte si například následující deklaraci sjednocení:
union U1(int, bool, DateTime);
Podle pravidla uvozovek je výchozí stav Value "not null". Ale to neodpovídá chování typu, default(U1).Value je null. Aby bylo možné chování převést, je příjemce nucen provést alespoň jeden typ případu s možnou hodnotou null. Něco jako:
union U1(int?, bool, DateTime);
To je ale pravděpodobně nežádoucí, spotřebitel nemusí chtít povolit explicitní vytváření s int? hodnotou.
Návrh: Odeberte pravidlo uvozovek, analýza s možnou hodnotou null by měla používat poznámky z Value vlastnosti k odvození výchozí nullability.
Rozlišení: Návrh je schválen.
[Vyřešeno] Sjednocení odpovídající hodnotě Nullable typu sjednocovací hodnoty
Pokud je příchozí hodnota vzoru typu sjednocení, obsah sjednocující hodnoty může být v závislosti na vzoru "unwrapped".
Měli bychom toto pravidlo rozšířit na scénáře, kdy příchozí hodnota vzoru je ?Nullable<union type>
Představte si následující scénář:
static bool Test1(StructUnion? u)
{
return u is 1;
}
static bool Test2(ClassUnion? u)
{
return u is 1;
}
Význam v u is 1 test1 a Test2 se velmi liší. V testu 1 se nejedná o sjednocování v test2.
Možná by "sjednocení párování" mělo "projít" Nullable<T> jako porovnávání vzorů obvykle v jiných situacích.
Pokud s tím jdeme, měl by sjednocovat odpovídající null vzor proti Nullable<union type> třídám fungovat stejně jako u tříd.
Tj. vzor je pravdivý, když (!nullableValue.HasValue || nullableValue.Value.Value is null).
Rozlišení: Návrh je schválen.
Co dělat s "špatnými" rozhraními API?
Co by měl kompilátor dělat s sjednocením odpovídajících rozhraní API, která vypadají jako shoda, ale jinak "špatně"? Kompilátor například najde TryGetValue/HasValue s odpovídajícím podpisem, ale je "špatný", protože požadovaný vlastní modifikátor nebo vyžaduje neznámou funkci atd. Měl by kompilátor bezobslužně ignorovat rozhraní API nebo nahlásit chybu? Podobně může být rozhraní API označeno jako zastaralé nebo experimentální. Měl by kompilátor hlásit jakoukoli diagnostiku, bezobslužně používat rozhraní API nebo rozhraní API bezobslužně používat?
Co když chybí typy pro deklaraci sjednocení
Co se stane, když UnionAttributeIUnion nebo IUnion<TUnion> chybí? Chyba? Syntetizovat? Něco jiného?
[Vyřešeno] Návrh obecného rozhraní IUnion
Byly provedeny argumenty, které IUnion<TUnion> by neměly dědit z IUnion parametru typu nebo omezit jeho typ na IUnion<TUnion>. Měli bychom se znovu vrátit.
Rozlišení: Rozhraní IUnion<TUnion> je prozatím odebráno.
[Vyřešeno] Typy hodnot s možnou hodnotou Null jako typy případů a jejich interakce s TryGetValue
Výše uvedená pravidla uvádějí, že pokud typ případu je typ hodnoty null, typ parametru použitý v odpovídající TryGetValue metodě by měl být podkladovým typem.
To je motivováno skutečností, že null hodnota by touto metodou nikdy nebyla výnosná. Na straně spotřeby není jako typ typu povolen typ s možnou hodnotou null, zatímco shoda s podkladovým typem by měla být schopna namapovat na volání této metody.
Měli bychom potvrdit, že souhlasíme s tímto zrušením.
Rozlišení: Dohodnuto/potvrzeno
Vzor ne boxingového sjednocení přístupu
Je potřeba zadat přesná pravidla pro vyhledání vhodných HasValue rozhraní API a TryGetValue rozhraní API.
Týká se dědičnosti? Je pro čtení a zápis HasValue přijatelná shoda? Atd.
[Vyřešeno] TryGetValue odpovídající převody
V oddílu Sjednocení párování je uvedeno:
Pro vzor, který naznačuje kontrolu konkrétního typu
T, pokudTryGetValue(S value)je metoda k dispozici, a existuje implicitní převod zTS, pak se tato metoda používá k získání hodnoty.
Je sada implicitních převodů omezena jakýmkoli způsobem? Jsou například povoleny uživatelem definované převody? A co převody řazené kolekce členů a jiné ne tak triviální převody? Některé z nich jsou dokonce standardní převody.
Je sada TryGetValue metod omezena jiným způsobem? Například oddíl Union Patterns znamená, že se považují pouze metody s typem parametru odpovídajícímu typu případu:
public bool TryGetValue(out T value)metoda pro každý typTpřípadu .
Bylo by dobré mít explicitní odpověď.
Rozlišení: Zvažují se pouze implicitní identity nebo odkazy nebo převody boxingu.
TryGetValue a analýza s možnou hodnotou null
Pokud vzor přístupu
HasValuebez rámečku neboTryGetValue(...)se používá k dotazování na obsah sjednocovacího typu (explicitně nebo prostřednictvím porovnávání vzorů), má vlivValuena stav nullability stejným způsobem, jako kdybyValuebyl zaškrtnutý přímo: StavValuenull ve větvi se změní natruehodnotu null.
Je sada TryGetValue metod omezena jakýmkoli způsobem? Například oddíl Union Patterns znamená, že se považují pouze metody s typem parametru odpovídajícímu typu případu:
public bool TryGetValue(out T value)metoda pro každý typTpřípadu .
Bylo by dobré mít explicitní odpověď.
Objasnění pravidel ohledně default hodnot typů sjednocení struktur
Poznámka: Výchozí pravidlo nullability uvedené níže bylo odebráno.
Poznámka: Byla odebrána pravidla "výchozího" dobře vytvořeného formátu, která jsou uvedena níže. Měli bychom potvrdit, že to je to, co chceme.
Oddíl s nulovou dostupností říká:
Pro sjednocovací typy, u kterých žádný z typů případů nemá hodnotu null, je výchozí stav
Value"not null" místo "možná null".
Vzhledem k tomu, že v následujícím příkladu se současná implementace považuje za Values2 "not null":
S2 s2 = default;
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => throw null!;
public S2(bool x) => throw null!;
object? System.Runtime.CompilerServices.IUnion.Value => throw null!;
}
Ve stejnou dobu oddíl well-formness říká:
- Výchozí hodnota: Pokud je sjednocovacího typu typ hodnoty, je to výchozí hodnota, která má
nullhodnotu .Value- Výchozí konstruktor: Pokud má typ sjednocení konstruktor nullary (no-argument), výsledná sjednocení má
nulljako jehoValue.
Implementace, jako je tato, bude v rozporu s chováním analýzy s možnou hodnotou null pro výše uvedený příklad.
Měla by být upravena pravidla dobře formování , nebo by měla být stav Valuedefault "možná null"?
Pokud druhá možnost, měla by inicializace S2 s2 = default; vyvolat upozornění s nulovou použitelností?
Ověřte, že parametr typu není nikdy sjednocovacího typu, i když je omezen na jeden.
class C1 : System.Runtime.CompilerServices.IUnion
{
private readonly object _value;
public C1(int x) { _value = x; }
public C1(string x) { _value = x; }
object System.Runtime.CompilerServices.IUnion.Value => _value;
}
class Program
{
static bool Test1<T>(T u) where T : C1
{
return u is int; // Not a union matching
}
static bool Test2<T>(T u) where T : C1
{
return u is string; // Not a union matching
}
}
Mají atributy po podmínce vliv na výchozí nulovou hodnotu instance Sjednocení?
Poznámka: Výchozí pravidlo nullability uvedené níže bylo odebráno. A z metod vytváření sjednocení už neodvozujeme výchozí hodnotu nullability Value vlastnosti. Otázka je proto zastaralá nebo již není použitelná pro aktuální návrh.
Pro sjednocovací typy, u kterých žádný z typů případů nemá hodnotu null, je výchozí stav
Value"not null" místo "možná null".
Očekává se upozornění v následujícím scénáři.
#nullable enable
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null!;
public S1([System.Diagnostics.CodeAnalysis.NotNull] bool? x) => throw null!;
object? System.Runtime.CompilerServices.IUnion.Value => throw null!;
}
class Program
{
static void Test2(S1 s)
{
// warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive).
// For example, the pattern 'null' is not covered.
_ = s switch { int => 1, bool => 3 }; //
}
}
Sjednocení převodů
[Vyřešeno] Kde patří mezi jiné převody podle priority?
Sjednocení převodů se cítí jako jiná forma převodu definovaného uživatelem. Aktuální implementace je proto klasifikuje hned po neúspěšném pokusu o klasifikaci implicitního převodu definovaného uživatelem a v případě existence se považuje za pouze jinou formu uživatelem definovaného převodu. To má následující důsledky:
- Implicitní převod definovaný uživatelem má přednost před sjednocovacího převodu.
- Při použití explicitního přetypování v kódu má explicitní převod definovaný uživatelem přednost před sjednocovacím převodem.
- Pokud kód neobsahuje explicitní přetypování, má převod sjednocení přednost před explicitním uživatelem definovaným převodem.
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => ...
public S1(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static implicit operator S1(int x) => ...
}
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(int x) => ...
public S2(string x) => ...
object System.Runtime.CompilerServices.IUnion.Value => ...
public static explicit operator S2(int x) => ...
}
class Program
{
static S1 Test1() => 10; // implicit operator S1(int x) is used
static S1 Test2() => (S1)20; // implicit operator S1(int x) is used
static S2 Test3() => 10; // Union conversion S2.S2(int) is used
static S2 Test4() => (S2)20; // explicit operator S2(int x)
}
Je potřeba ověřit, že se jedná o chování, které se nám líbí. Jinak by měla být upřesněna pravidla převodu.
Řešení:
Schváleno pracovní skupinou.
[Vyřešeno] Ref-ness parametru konstruktoru
Jazyk v současné době umožňuje pouze podle hodnot a in parametrů pro uživatelem definované operátory převodu.
Zdá se, že důvody tohoto omezení se vztahují také na konstruktory vhodné pro sjednocovací převody.
Návrh:
Upravte definici oddílu case type constructor výše Union types :
-For each public constructor with exactly one parameter, the type of that parameter is considered a *case type* of the union type.
+For each public constructor with exactly one **by-value or `in`** parameter, the type of that parameter is considered a *case type* of the union type.
Řešení:
Nyní schválila pracovní skupina. Můžeme ale zvážit "rozdělení" sady konstruktorů typů případů a sady konstruktorů vhodných pro převody typů sjednocení.
[Vyřešeno] Převody s možnou hodnotou null
Oddíl Převody s možnou hodnotou null explicitně uvádí převody, které lze použít jako podkladové. Aktuální specifikace nenavrhuje žádné úpravy daného seznamu. Výsledkem je chyba pro následující scénář:
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1? Test1(int x)
{
return x; // error CS0029: Cannot implicitly convert type 'int' to 'S1?'
}
}
Návrh:
Upravte specifikaci tak, aby podporovala implicitní převod s možnou hodnotou null z S na T? základě sjednocovacího převodu.
Konkrétně za předpokladu, že je sjednocovacího T typu implicitní převod na typ T? z typu nebo výrazu E , pokud existuje sjednocovacího převodu z E typu C a C je to typ Tpřípadu .
Všimněte si, že neexistuje žádný požadavek, aby typ E hodnoty, který nemá hodnotu null.
Převod se vyhodnotí jako podkladový sjednocovací převod z S na T následný zabalení z T do T?
Řešení:
Schválených.
[Vyřešeno] Zrušené převody
Chceme upravit oddíl lifted převodů tak, aby podporoval zdvižené sjednocované převody? V současné době nejsou povolené:
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(int x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(int? x)
{
return x; // error CS0029: Cannot implicitly convert type 'int?' to 'S1'
}
static S1? Test2(int? y)
{
return y; // error CS0029: Cannot implicitly convert type 'int?' to 'S1?'
}
}
Řešení:
Prozatím nejsou žádné zdvižené převody sjednocení. Některé poznámky z diskuze:
Analogie k uživatelsky definovaným převodům se tady trochu rozdělí. Obecně platí, že sjednocení můžou obsahovat hodnotu null, která přichází. Není jasné, zda by měla zvedání vytvořit instanci sjednocovacího typu s
nullhodnotou uloženou v ní, nebo zda by měla vytvořitnullhodnotuNullable<Union>.
[Vyřešeno] Blokovat sjednocování převodu z instance základního typu?
Aktuální chování může být matoucí:
struct S1 : System.Runtime.CompilerServices.IUnion
{
public S1(System.ValueType x)
{
}
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(System.ValueType x)
{
return x; // Union conversion
}
static S1 Test2(System.ValueType y)
{
return (S1)y; // Unboxing conversion
}
}
Všimněte si, že jazyk explicitně zakazuje deklarování uživatelsky definovaných převodů ze základního typu. Proto může dojít k tomu, že nepovolí sjednocovat převody, jako je tato.
Řešení:
Prozatím nic zvláštního. Obecné scénáře nelze přesto plně chránit.
[Vyřešeno] Blokovat sjednocování převodu z instance typu rozhraní?
Aktuální chování může být matoucí:
struct S1 : I1, System.Runtime.CompilerServices.IUnion
{
public S1(I1 x) => throw null;
public S1(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
interface I1 { }
struct S2 : System.Runtime.CompilerServices.IUnion
{
public S2(I1 x) => throw null;
public S2(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class C3 : System.Runtime.CompilerServices.IUnion
{
public C3(I1 x) => throw null;
public C3(string x) => throw null;
object System.Runtime.CompilerServices.IUnion.Value => throw null;
}
class Program
{
static S1 Test1(I1 x)
{
return x; // Union conversion
}
static S1 Test2(I1 x)
{
return (S1)x; // Unboxing
}
static S2 Test3(I1 x)
{
return x; // Union conversion
}
static S2 Test4(I1 x)
{
return (S2)x; // Union conversion
}
static C3 Test3(I1 x)
{
return x; // Union conversion
}
static C3 Test4(I1 x)
{
return (C3)x; // Reference conversion
}
}
Všimněte si, že jazyk explicitně zakazuje deklarování uživatelsky definovaných převodů ze základního typu. Proto může dojít k tomu, že nepovolí sjednocovat převody, jako je tato.
Řešení:
Prozatím nic zvláštního. Obecné scénáře nelze přesto plně chránit.
Obor názvů rozhraní IUnion
Obsahující obor názvů pro IUnion rozhraní zůstává nezadaný. Pokud má záměr zachovat ho v global oboru názvů, řekněme, že explicitně.
Návrh: Pokud je něco jednoduše přehlédnuto, mohli bychom použít System.Runtime.CompilerServices obor názvů.
Třídy jako Union typy
[Vyřešeno] Kontrola samotné instance pro null
Pokud je sjednocovacího typu typ třídy, může být hodnota sama o sobě null. A co tedy kontroly null?
Vzor null byl spolusouhlasován ke kontrole Value vlastnosti, takže jak zkontrolujete, že samotná sjednocení nemá hodnotu null?
Například:
- Pokud
SjeUnionstruktura, pro hodnotuS?jetruepouze tehdy,s is nullkdyžsjenullsám o sobě . KdyžCjeUniontřída,c is nullpro hodnotuC?je, kdyžcjefalsenullsám , ale jetrue, kdyžcsám nenínullac.Valuejenull.
Další příklad:
class C1 : IUnion
{
private readonly object? _value;
public C1(){}
public C1(int x) { _value = x; }
public C1(string x) { _value = x; }
object? IUnion.Value => _value;
}
class Program
{
static int Test1(C1? u)
{
// warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive).
// For example, the pattern 'null' is not covered.
// This is very confusing, the switch expression is indeed not exhaustive (u itself is not
// checked for null), but there is a case 'null => 3' in the switch expression.
// It looks like the only way to shut off the warning is to use 'case _'. Adding it removes
// all benefits of exhaustiveness checking, any union case could be missing and there would
// be no diagnostic about that.
return u switch { int => 1, string => 2, null => 3 };
}
}
Tato část návrhu je jasně optimalizovaná podle očekávání, že typ sjednocení je struktura. Některé možnosti:
- Škoda. Použije se
==pro kontrolu hodnoty null místo shody vzoru. -
nullNechte vzor (a implicitní vrácení null check v jiných vzorech) použít jak pro sjednocovat hodnotu, tak jehoValuevlastnost:u is null ==> u == null || u.Value == null. - Nepovolují se třídám, aby se sjednocovat typy!
[Vyřešeno] Odvození z Union třídy
Když třída používá Uniontřídu jako svou základní třídu podle aktuální specifikace, stane se Unionsamotnou třídou. K tomu dochází, protože automaticky dědí implementaci IUnion rozhraní, není nutné ji znovu implementovat. Současně konstruktory odvozeného typu definují sadu typů v této nové Union. Je velmi snadné dostat se k velmi podivné jazykové chování kolem těchto dvou tříd:
class C1 : IUnion
{
private readonly object _value;
public C1(long x) { _value = x; }
public C1(string x) { _value = x; }
object IUnion.Value => _value;
}
class C2(int x) : C1(x);
class Program
{
static int Test1(C1 u)
{
// Good
return u switch { long => 1, string => 2, null => 3 };
}
static int Test2(C2 u)
{
// error CS8121: An expression of type 'C2' cannot be handled by a pattern of type 'long'.
// error CS8121: An expression of type 'C2' cannot be handled by a pattern of type 'string'.
return u switch { long => 1, string => 2, null => 3 };
}
}
Některé možnosti:
Změna, když je
Uniontyp třídy typ. Třída jeUnionnapříklad typ, pokud má hodnotu true:- Je
sealedto proto, že odvozené typy se nebudou považovat zaUniontypy, což je matoucí. - Žádná z jejích základů neimplementuje
IUnion
To ještě není dokonalé. Pravidla jsou příliš malá. Je snadné udělat chybu. V deklaraci není žádná diagnostika, ale
Unionpárování nefunguje.- Je
Zákaz sjednocování tříd.
[Vyřešeno] Operátor is-type
Operátor is-type je určen jako kontrola typu modulu runtime. Syntakticky vypadá velmi podobně jako vzor typu, ale není to.
UnionProto se speciální párování nepoužije, což by mohlo vést k nejasnostem uživatele.
struct S1 : IUnion
{
private readonly object _value;
public S1(int x) { _value = x; }
public S1(string x) { _value = x; }
object IUnion.Value => _value;
}
class Program
{
static bool Test1(S1 u)
{
return u is int; // warning CS0184: The given expression is never of the provided ('int') type
}
static bool Test2(S1 u)
{
return u is string and ['1', .., '2']; // Good
}
}
V případě rekurzivní sjednocení nemusí vzor typu zobrazovat žádné upozornění, ale přesto nebude dělat to, co si uživatel může myslet, že by to udělal.
Rozlišení: Měl by fungovat jako vzor typu.
Vzor seznamu
Vzor seznamu vždy selže s odpovídajícími funkcemi Union :
struct S1 : IUnion
{
private readonly object _value;
public S1(int[] x) { _value = x; }
public S1(string[] x) { _value = x; }
object IUnion.Value => _value;
}
class Program
{
static bool Test1(S1 u)
{
// error CS8985: List patterns may not be used for a value of type 'object'. No suitable 'Length' or 'Count' property was found.
// error CS0021: Cannot apply indexing with [] to an expression of type 'object'
return u is [10];
}
}
static class Extensions
{
extension(object o)
{
public int Length => 0;
}
}
Další otázky
- Jak použití konstruktorů v sjednocovací převody, tak použití porovnávání vzorů
TryGetValue(...)sjednocení je určeno tak, aby bylo při použití více konstruktorů lenientní: Stačí vybrat jednu. To by nemělo být na pravidlech dobře formulované, ale nevadí nám to? - Specifikace subtly spoléhá na implementaci
IUnion.Valuevlastnosti namísto jakékoliValuevlastnosti nalezené na samotném typu sjednocení. Cílem je poskytnout větší flexibilitu pro existující typy (které můžou mít vlastníValuevlastnost pro jiné použití) k implementaci modelu. Je to ale nepříjemné a nekonzistentní s tím, jak se ostatní členové nacházejí a používají přímo u typu sjednocení. Měli bychom udělat změnu? Další možnosti:- Vyžadovat typy sjednocení k zveřejnění veřejné
Valuevlastnosti. - Preferujte veřejnou
Valuevlastnost, pokud existuje, ale vraťte se zpět k implementaciIUnion.Value(podobně jakoGetEnumeratorpravidla).
- Vyžadovat typy sjednocení k zveřejnění veřejné
- Navrhovaná syntaxe deklarace sjednocení není všeobecně oblíbená, zejména pokud jde o vyjádření typů případů. Alternativy se zatím také setkávají s kritikou, ale je možné, že nakonec uděláme změnu. Některé hlavní obavy, které se týkají aktuálního:
- Čárky jako oddělovače mezi typy písmen se můžou zdát, že pořadí záleží.
- Závorky vypadají příliš podobně jako primární konstruktory (navzdory tomu, že nemají názvy parametrů).
- Příliš se liší od výčtů, které mají jejich "případy" ve složených závorkách.
- Zatímco deklarace sjednocení generují struktury s jedním referenčním polem, jsou stále poněkud náchylné k neočekávanému chování při použití v souběžných kontextech. Pokud například člen funkce definovaný uživatelem více než jednou přeřazuje, může být mezi těmito dvěma přístupy
thisznovu přiřazena proměnná obsahující proměnná jako celek jiným vláknem. Kompilátor může vygenerovat kód, který bude v případě potřeby kopírovatthisdo místního prostředí. Měla by? Obecně platí, jaký stupeň odolnosti souběžnosti je žádoucí a přiměřeně dosažitelný?
C# feature specifications