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.
8.1 Obecné
Typy jazyka C# jsou rozdělené do dvou hlavních kategorií: typ odkazu a typ hodnoty. Typ hodnoty nebo odkazový typ může být obecný typ, který přebírá jeden nebo více parametrů typu. Parametry typu mohou určit oba typy hodnot i odkazové typy.
type
: reference_type
| value_type
| type_parameter
| pointer_type // unsafe code support
;
pointer_type (§24.3.1) je k dispozici pouze v nebezpečném kódu (§24).
Typy hodnot se liší od odkazových typů v tom, že proměnná hodnotových typů přímo obsahuje data, zatímco proměnná referenčního typu ukládá odkaz na jeho data, druhá se označuje jako objekt. U referenčních typů je možné, aby dvě proměnné odkazovaly na stejný objekt, a proto operace s jednou proměnnou ovlivnily objekt odkazovaný druhou proměnnou. U hodnotových typů mají proměnné svoji vlastní kopii dat a není možné, aby operace na jedné z nich ovlivnily druhou.
Poznámka: Pokud je proměnná odkazem nebo výstupním parametrem, nemá vlastní úložiště, ale odkazuje na úložiště jiné proměnné. V tomto případě je ref nebo out proměnná v podstatě aliasem pro jinou proměnnou, nikoli odlišnou proměnnou. koncová poznámka
Systém typů jazyka C# je sjednocený tak, aby s hodnotou jakéhokoli typu bylo možné zacházet jako s objektem. Každý typ v jazyce C# je přímo nebo nepřímo odvozen od object typu třídy a object je konečným základním typem všech typů. Hodnoty referenčních typů jsou považovány za objekty jednoduše zobrazením hodnot jako typu object. Hodnoty hodnotových typů jsou považovány za objekty provedením operací boxování a rozbalení (§8.3.13).
V rámci této specifikace jsou některé názvy typů knihoven napsané bez použití jejich úplné kvalifikace. Další informace najdete v části §C.5 .
8.2 Odkazové typy
8.2.1 Obecné
Typ odkazu je typ třídy, typ rozhraní, typ pole, typ delegáta dynamic , typ nebo jakýkoli parametr typu, který je omezen na odkazový typ (tj. jakýkoli parametr typu s omezením typu odkazu nebo omezením typu třídy (§15.2.5)). Pro každý typ odkazu, který není nullable, existuje odpovídající typ odkazu s možnou hodnotou null, který je zaznamenán připojením ? k názvu typu.
reference_type
: non_nullable_reference_type
| nullable_reference_type
;
non_nullable_reference_type
: class_type
| interface_type
| array_type
| delegate_type
| 'dynamic'
;
class_type
: type_name
| 'object'
| 'string'
;
interface_type
: type_name
;
array_type
: non_array_type rank_specifier+
;
non_array_type
: value_type
| class_type
| interface_type
| delegate_type
| 'dynamic'
| type_parameter
| pointer_type // unsafe code support
;
rank_specifier
: '[' ','* ']'
;
delegate_type
: type_name
;
nullable_reference_type
: non_nullable_reference_type nullable_type_annotation
;
nullable_type_annotation
: '?'
;
pointer_type (§24.3.1) je k dispozici pouze v nebezpečném kódu (§24.3). nullable_reference_type dále probíráme v §8.9.
Hodnota typu odkazu je odkaz na instanci typu, která se označuje jako objekt. Speciální hodnota null je kompatibilní se všemi odkazovými typy a označuje nepřítomnost instance.
8.2.2 Typy tříd
Typ třídy definuje datovou strukturu, která obsahuje datové členy(konstanty a pole), členy funkce(metody, vlastnosti, události, indexery, operátory, konstruktory instancí, finalizátory a statické konstruktory) a vnořené typy. Typy tříd podporují dědičnost, mechanismus, kdy odvozené třídy mohou rozšířit a specializovat základní třídy. Instance typů tříd se vytvářejí pomocí object_creation_expression s (§12.8.17.2).
Typy tříd jsou popsány v §15.
Některé předdefinované typy tříd mají v jazyce C# zvláštní význam, jak je popsáno v následující tabulce.
| Typ třídy | Popis |
|---|---|
System.Object |
Konečná základní třída všech ostatních typů. Viz §8.2.3. |
System.String |
Typ řetězce jazyka C#. Viz §8.2.5. |
System.ValueType |
Základní třída všech typů hodnot. Viz §8.3.2. |
System.Enum |
Základní třída všech enum typů. Viz §20.5. |
System.Array |
Základní třída všech typů polí. Viz §17.2.2. |
System.Delegate |
Základní třída všech delegate typů. Viz §21.1. |
System.Exception |
Základní třída všech typů výjimek. Viz §22.3. |
8.2.3 Typ objektu
Typ object třídy je konečným základním typem všech ostatních typů. Každý typ v jazyce C# je přímo nebo nepřímo odvozen od object typu třídy.
Klíčové slovo object je jednoduše alias pro předdefinovanou třídu System.Object.
8.2.4 Dynamický typ
Typ dynamic , například object, může odkazovat na libovolný objekt. Při použití operací na výrazy typu dynamicse jejich řešení odloží, dokud program neběží. Proto pokud operaci nelze oprávněně použít na odkazovaný objekt, během kompilace se nezobrazí žádná chyba. Místo toho dojde k výjimce při selhání řešení operace za běhu.
Typ dynamic je dále popsán v §8.7 a dynamické vazby v §12.3.1.
8.2.5 Typ řetězce
Typ string je zapečetěný typ třídy, který dědí přímo z object.
string Instance třídy představují řetězce znaků Unicode.
string Hodnoty typu lze zapsat jako řetězcové literály (§6.4.5.6).
Klíčové slovo string je jednoduše alias pro předdefinovanou třídu System.String.
8.2.6 Typy rozhraní
Rozhraní definuje kontrakt. Třída nebo struktura, která implementuje rozhraní, musí dodržovat svou smlouvu. Rozhraní může dědit z více základních rozhraní a třída nebo struktura může implementovat více rozhraní.
Typy rozhraní jsou popsány v §19.
8.2.7 Typy polí
Pole je datová struktura, která obsahuje nula nebo více proměnných, ke kterým se přistupuje prostřednictvím vypočítaných indexů. Proměnné obsažené v matici, označované také jako prvky pole, jsou všechny stejného typu a tento typ se nazývá typ prvku pole.
Typy polí jsou popsány v §17.
8.2.8 Typy delegátů
Delegát je datová struktura, která odkazuje na jednu nebo více metod. Například metody odkazují také na jejich odpovídající instance objektu.
Poznámka: Nejbližší ekvivalent delegáta v jazyce C nebo C++ je ukazatel funkce, ale zatímco ukazatel funkce může odkazovat pouze na statické funkce, delegát může odkazovat na statické i instance metody. V druhém případě delegát ukládá nejen odkaz na vstupní bod metody, ale také odkaz na instanci objektu, na které se má metoda vyvolat. koncová poznámka
Typy delegátů jsou popsány v §21.
8.3 Typy hodnot
8.3.1 Obecné
Typ hodnoty je buď typ struktury, nebo typ výčtu. Jazyk C# poskytuje sadu předdefinovaných typů struktur označovaných jako jednoduché typy. Jednoduché typy jsou identifikovány prostřednictvím klíčových slov a kontextových klíčových slov.
value_type
: non_nullable_value_type
| nullable_value_type
;
non_nullable_value_type
: struct_type
| enum_type
;
struct_type
: type_name
| simple_type
| tuple_type
;
simple_type
: numeric_type
| 'bool'
;
numeric_type
: integral_type
| floating_point_type
| 'decimal'
;
integral_type
: 'sbyte'
| 'byte'
| 'short'
| 'ushort'
| 'int'
| 'uint'
| 'nint'
| 'nuint'
| 'long'
| 'ulong'
| 'char'
;
floating_point_type
: 'float'
| 'double'
;
tuple_type
: '(' tuple_type_element (',' tuple_type_element)+ ')'
;
tuple_type_element
: type identifier?
;
enum_type
: type_name
;
nullable_value_type
: non_nullable_value_type nullable_type_annotation
;
Vzhledem k tomu, že názvy nint a nuint nejsou klíčovými slovy, existuje syntaktická nejednoznačnost mezi jejich uznáním jako type_name nebo value_type. Je-li překlad typu (§7.8.1) u některého z těchto názvů úspěšný, je tento název považován za type_name; jinak se uznává jako value_type.
Na rozdíl od proměnné typu odkazu může proměnná typu hodnoty obsahovat hodnotu null pouze v případě, že je typ hodnoty nullable (§8.3.12). Pro každý typ hodnoty s možnou hodnotou null existuje odpovídající typ hodnoty null označující stejnou sadu hodnot plus hodnotu null.
Přiřazení proměnné typu hodnoty vytvoří kopii přiřazené hodnoty. Liší se od přiřazení k proměnné typu odkazu, která kopíruje odkaz, ale nikoli objekt identifikovaný odkazem.
8.3.2 Typ System.ValueType
Všechny typy hodnot implicitně dědí z objektu classSystem.ValueType, který následně dědí z třídy object. Žádný typ nelze odvodit z typu hodnoty a typy hodnot jsou tedy implicitně zapečetěné (§15.2.2.3).
Všimněte si, že System.ValueType se nejedná o value_type. Spíše se jedná o class_type , ze kterého jsou automaticky odvozeny všechny value_type.
8.3.3 Výchozí konstruktory
Všechny typy hodnot mají veřejný konstruktor instance bez parametrů, který se nazývá výchozí konstruktor. Pro typy struktur, které explicitně deklarují konstruktor instance bez parametrů, je výchozí konstruktor syntetizován kompilátorem. Výchozí konstruktor vrátí instanci s nulovou inicializovanou hodnotou, která se označuje jako výchozí hodnota pro typ hodnoty:
- Pro všechny simple_types je výchozí hodnota hodnota vytvořená bitovým vzorem všech nul:
- Pro
sbyte,byte,short, ,ushort, ,intuintnintnuintlongaulong, výchozí hodnota je .0 - Pro
char, výchozí hodnota je'\x0000'. - Pro
float, výchozí hodnota je0.0f. - Pro
double, výchozí hodnota je0.0d. - Pro
decimal, výchozí hodnota je (to znamená0mhodnota nula s měřítkem 0). - Pro
bool, výchozí hodnota jefalse. -
Pro enum_type
Eje0výchozí hodnota převedena na typE.
- Pro
-
Výchozí hodnota pro struct_type je hodnota vytvořená nastavením všech polí typu hodnoty na výchozí hodnotu a všech polí typu odkazu na
null. -
Pro nullable_value_type výchozí hodnota je instance, pro kterou
HasValueje vlastnost false. Výchozí hodnota se označuje také jako hodnota null typu hodnoty s možnou hodnotou null. Pokus o přečteníValuevlastnosti takové hodnoty způsobí vyvolání výjimky typuSystem.InvalidOperationException(§8.3.12).
Stejně jako jakýkoli jiný konstruktor instance je výchozí konstruktor typu hodnoty vyvolán pomocí operátoru new .
Poznámka: Z důvodů efektivity není tento požadavek určen k tomu, aby implementace vygenerovala volání konstruktoru. U hodnotových typů, které nemají explicitně deklarovaný konstruktor instance bez parametrů, vytvoří výchozí výraz hodnoty (§12.8.21) stejný výsledek jako při použití výchozího konstruktoru. Pro typy struktur, které deklarují explicitní konstruktor instance bez parametrů,
defaultvytvoří nulovou inicializovanou výchozí hodnotu, zatímconew S()vyvolá deklarovaný konstruktor a výsledky se mohou lišit. koncová poznámka
Příklad: V následujícím kódu jsou proměnné
ijakvšechny inicializovány na nulu.class A { void F() { int i = 0; int j = new int(); int k = default(int); } }konec příkladu
Typ struktury je povolen deklarovat konstruktory instance, včetně konstruktoru instance bez parametrů. Explicitně deklarovaný konstruktor instance bez parametrů musí mít veřejnou přístupnost (§16.5.9).
8.3.4 Typy struktur
Typ struktury je typ hodnoty, který může deklarovat konstanty, pole, metody, vlastnosti, události, indexery, operátory, konstruktory instancí, statické konstruktory a vnořené typy. Prohlášení typů struktur je popsáno v §16.
8.3.5 Jednoduché typy
nint Kromě a nuint, jednoduché typy jsou aliasy pro předdefinované struct typy v System oboru názvů, jak je popsáno v následující tabulce.
| Klíčové slovo | Typ aliasu |
|---|---|
sbyte |
System.SByte |
byte |
System.Byte |
short |
System.Int16 |
ushort |
System.UInt16 |
int |
System.Int32 |
uint |
System.UInt32 |
nint |
Žádný; viz níže |
nuint |
Žádný; viz níže |
long |
System.Int64 |
ulong |
System.UInt64 |
char |
System.Char |
float |
System.Single |
double |
System.Double |
bool |
System.Boolean |
decimal |
System.Decimal |
Každý jednoduchý typ má členy. Každý jednoduchý typ, který je aliasem pro předdefinovaný typ struktury, má členy daného typu struktury.
Příklad:
intmá všechny členy specifické pro implementaci deklarované vSystem.Int32a členy (povinné a implementace specifické) zděděné zSystem.Objecta následující příkazy jsou povoleny:int i = int.MaxValue; // System.Int32.MaxValue constant string s = i.ToString(); // System.Int32.ToString() instance method string t = 123.ToString(); // System.Int32.ToString() instance methodkonec příkladu
Poznámka: Jednoduché typy se liší od jiných typů struktur v tom, že umožňují určité další operace:
- Většina jednoduchých typů umožňuje vytváření hodnot zápisem literálů (§6.4.5), i když jazyk C# obecně neposkytuje literály typů struktur. Příklad:
123je literál typuinta'a'je literál typuchar. konec příkladu- Pokud jsou operandy výrazu všechny jednoduché konstanty typu, je možné, aby kompilátor vyhodnotil výraz v době kompilace. Takový výraz se označuje jako constant_expression (§12.26). Výrazy zahrnující operátory definované jinými typy struktur se nepovažují za konstantní výrazy.
- Prostřednictvím
constdeklarací je možné deklarovat konstanty jednoduchých typů (§15.4). Není možné mít konstanty jiných typů struktur, ale podobný účinek poskytuje statická pole jen pro čtení.- Převody zahrnující jednoduché typy se mohou účastnit vyhodnocení operátorů převodu definovaných jinými typy struktur, ale uživatelem definovaný operátor převodu se nikdy nemůže účastnit vyhodnocení jiného uživatelem definovaného operátoru převodu (§10.5.3).
ukončit poznámku.
nint Typy a nuint jsou reprezentovány typy System.IntPtr a System.UIntPtrv uvedeném pořadí a nejsou aliasy pro tyto typy. V tomto kontextu jsou reprezentovány prostřednictvím:
- Pouze členové jsou přímo přístupní a
nintnuintjsou požadovanými metodamiObject(§C.2). K těmto typům mají přístup všichni ostatní členovéSystem.IntPtraSystem.UIntPtrmohou k němu přistupovat. - Operace prováděné vazbou
dynamicSystem.IntPtraSystem.UIntPtrhodnotami nemají přístup k operátorům anintoperátorůmnuint. - Ve všech ostatních ohledech
nintanuintchovat se, jako by se jedná oSystem.IntPtraliasy aSystem.UIntPtr.
8.3.6 Integrální typy
Jazyk C# podporuje následující integrální typy s velikostmi a rozsahy hodnot, jak je znázorněno níže:
- Typ
sbytepředstavuje 8bitové celé číslo se signem s hodnotami od-128do127včetně. - Typ
bytepředstavuje 8bitové celé číslo bez znaménka s hodnotami od0do255včetně. - Typ
shortpředstavuje 16bitové celé číslo se signedms a hodnotami od-32768do32767včetně. - Typ
ushortpředstavuje 16bitové celé číslo bez znaménka s hodnotami od0do65535včetně. - Typ
intpředstavuje 32bitové celé číslo s hodnotami od-2147483648do2147483647včetně. - Typ
uintpředstavuje 32bitové celé číslo bez znaménka s hodnotami od0do4294967295včetně. - Typ
nintpředstavuje nativní celé číslo se signepsem , jehož velikost a rozsah hodnot jsou definovány implementací, ale které musí být buď zintnebolong. - Typ
nuintpředstavuje nativní celé číslo bez znaménka , jehož velikost a rozsah hodnot jsou definovány implementací, ale který musí být buď zuintneboulong. Velikost nativního celého čísla bez znaménka musí být stejná jako nativní celé číslo se znaménkem. - Typ
longpředstavuje 64bitové celé číslo se 64bitovými hodnotami od-9223372036854775808do9223372036854775807včetně. - Typ
ulongpředstavuje 64bitové celé číslo bez znaménka s hodnotami od0do18446744073709551615včetně. - Typ
charpředstavuje 16bitové celé číslo bez znaménka s hodnotami od0do65535včetně. Sada možných hodnot prochartyp odpovídá znakové sadě Unicode.Poznámka: Ačkoli
charmá stejnou reprezentaci jakoushort, ne všechny operace povolené na jednom typu jsou povoleny na druhém. koncová poznámka
Všechny celočíselné typy se píšou pomocí formátu doplňku dvou.
Integral_type unární a binární operátory vždy pracují se znaménkem 32bitovou přesností, bez znaménka 32bitovou přesností, 64bitovou přesností, nepodepsanou 64bitovou přesností, nativní podepsanou přesností nebo nativní přesností bez znaménka, jak je popsáno v §12.4.7.
Poznámka: Nativní přesnost znamená 32bitovou verzi na 32bitových platformách a 64bitovou verzi na 64bitových platformách. Operátory používají
nintnuintnativní přesnost místo povýšení na větší typ. koncová poznámka
Typ char je klasifikován jako celočíselný typ, ale liší se od ostatních integrálních typů dvěma způsoby:
- Neexistují žádné předdefinované implicitní převody z jiných typů na
chartyp. Konkrétně platí, že i když majíbytetypyushortrozsahy hodnot, které jsou plně reprezentovatelné pomocíchartypu, implicitní převody z sbajtu, bajtu neboushortcharneexistují. - Konstanty typu musí být zapsány
charjako character_literals nebo jako integer_literalv kombinaci s přetypování na znak typu.
Příklad:
(char)10je stejný jako'\x000A'. konec příkladu
Operátory checked a unchecked příkazy slouží k řízení kontroly přetečení u aritmetických operací a převodů celočíselného typu (§12.8.20).
checked V kontextu vyvolá přetečení chybu v době kompilace nebo vyvolá System.OverflowException vyvolání.
unchecked V kontextu se přetečení ignorují a všechny bity s vysokým pořadím, které se nevejdou do cílového typu, se zahodí.
8.3.7 Typy s plovoucí desetinou čárkou
Jazyk C# podporuje dva typy s plovoucí desetinou čárkou: float a double. Tyto float typy double jsou reprezentovány pomocí 32bitových formátů s jednoduchou přesností a 64bitovou dvojitou přesností IEC 60559, které poskytují následující sady hodnot:
- Kladná nula a záporná nula. Ve většině situací se kladná nula a záporná nula chovají stejně jako jednoduchá hodnota nula, ale některé operace rozlišují mezi těmito dvěma operacemi (§12.13.3).
- Kladné nekonečno a záporné nekonečno. Infinity se vytvářejí pomocí takových operací, jako je dělení nenulového čísla nulou.
Příklad:
1.0 / 0.0získá kladné nekonečno a–1.0 / 0.0získá záporné nekonečno. konec příkladu - Hodnota Not-a-Number , často zkrácená NaN. Sítě NAN se vytvářejí neplatnými operacemi s plovoucí desetinnou čárkou, jako je například dělení nuly nulou.
- Konečná sada nenulových hodnot formuláře s × m × 2e, kde s je 1 nebo −1, a m a e jsou určeny konkrétním typem s plovoucí desetinnou čárkou: Pro , 0
float2²⁴ a −149 ≤ < ≤ 104 a pro , 0< 2⁵³ a −1075 ≤ e ≤ 970.double< Denormalizovaná čísla s plovoucí desetinnou čárkou se považují za platné nenulové hodnoty. Jazyk C# nevyžaduje ani nezakazuje, aby odpovídající implementace podporovala denormalizovaná čísla s plovoucí desetinnou čárkou.
Typ float může představovat hodnoty od přibližně 1,5 × 10⁻⁴⁵ do 3,4 × 10³⁸ s přesností na 7 číslic.
Typ double může představovat hodnoty od přibližně 5,0 × 10⁻³²⁴ do 1,7 × 10³⁰⁸ s přesností 15-16 číslic.
Pokud je buď operand binárního operátoru typu s plovoucí desetinnou čárkou, použijí se standardní číselné povýšení, jak je uvedeno v §12.4.7, a operace se provádí s přesností nebo float s přesnostídouble.
Operátory s plovoucí desetinou čárkou, včetně operátorů přiřazení, nikdy nevyvolávají výjimky. Ve výjimečných situacích generují operace s plovoucí desetinnou čárkou nulu, nekonečno nebo N, jak je popsáno níže:
- Výsledek operace s plovoucí desetinnou čárkou se zaokrouhlí na nejbližší reprezentovatelnou hodnotu v cílovém formátu.
- Pokud je velikost výsledku operace s plovoucí desetinou čárkou pro cílový formát příliš malá, výsledek operace se změní na kladnou nulu nebo zápornou nulu.
- Pokud je velikost výsledku operace s plovoucí desetinou čárkou příliš velká pro cílový formát, výsledek operace se změní na kladné nekonečno nebo záporné nekonečno.
- Pokud je operace s plovoucí desetinnou čárkou neplatná, stane se výsledkem operace NaN.
- Pokud je jeden nebo oba operandy operace s plovoucí desetinnou čárkou naN, stane se výsledkem operace NaN.
Operace s plovoucí desetinnou čárkou mohou být prováděny s vyšší přesností než typ výsledku operace. Chcete-li vynutit hodnotu typu s plovoucí desetinnou čárkou na přesnou přesnost jeho typu, lze použít explicitní přetypování (§12.9.8).
Příklad: Některé hardwarové architektury podporují "rozšířený" nebo "dlouhý dvojitý" typ s plovoucí desetinnou čárkou s větší rozsahem a přesností než
doubletyp, a implicitně provádějí všechny operace s plovoucí desetinnou čárkou pomocí tohoto vyššího typu přesnosti. Pouze při nadměrném výkonu může být taková hardwarová architektura provedena za účelem provádění operací s plovoucí desetinnou čárkou s menší přesností a nevyžaduje implementaci, která by snížila výkon i přesnost, jazyk C# umožňuje použití vyššího typu přesnosti pro všechny operace s plovoucí desetinnou čárkou. Kromě poskytování přesnějších výsledků to má zřídka žádné měřitelné účinky. Nicméně, ve výrazech formulářex * y / z, kde násobení vytvoří výsledek, který je mimodoublerozsah, ale následné dělení vrátí dočasný výsledek zpět dodoubleoblasti, skutečnost, že výraz je vyhodnocen ve vyšším formátu rozsahu, může způsobit konečný výsledek vytvořit místo nekonečna. konec příkladu
8.3.8 Desetinný typ
Typ decimal je 128bitový datový typ vhodný pro finanční a peněžní výpočty. Typ decimal může představovat hodnoty včetně hodnot v rozsahu nejméně -7,9 × 10⁻²⁸ až 7,9 × 10²⁸ s alespoň 28místnou přesností.
Konečná sada hodnot typu decimal má tvar (–1)v × c × 10⁻e, pokud je znaménko v 0 nebo 1, koeficient c je dán 0 ≤ < a měřítko e je takové, aby Emin ≤ e ≤ Emax, kde Cmax je alespoň 1 × 10²⁸, Emin ≤ 0, a Emax ≥ 28. Typ decimal nemusí nutně podporovat nuly, infinity nebo naN.
A decimal je reprezentováno jako celé číslo škálované hodnotou deseti. Pro decimals absolutní hodnotou menší než 1.0mje hodnota je přesná na alespoň 28. desetinné místo. Pro decimals absolutní hodnotou větší nebo rovnou 1.0mje hodnota přesná na nejméně 28 číslic. Na rozdíl od datovýchtypůchm floatdouble0.1 V reprezentacích floatdouble mají taková čísla často neukončující binární rozšíření, takže tyto reprezentace jsou náchylnější k chybám zaokrouhlování.
Pokud je jeden operand binárního operátoru typu decimal , použijí se standardní číselné povýšení, jak je uvedeno v §12.4.7, a operace se provádí s přesností decimal .
Výsledkem operace s hodnotami typu decimal je to, že výsledkem výpočtu přesného výsledku (zachování měřítka definovaného pro jednotlivé operátory) a následné zaokrouhlení tak, aby odpovídalo reprezentaci. Výsledky se zaokrouhlují na nejbližší reprezentovatelnou hodnotu a pokud je výsledek stejně blízko dvou reprezentovatelných hodnot, zaokrouhluje se na hodnotu, která má sudé číslo v nejméně významné pozici číslice (označuje se jako zaokrouhlení bankovního operátora). To znamená, že výsledky jsou přesné na alespoň 28. desetinné místo. Všimněte si, že zaokrouhlování může vytvořit nulovou hodnotu z nenulové hodnoty.
decimal Pokud aritmetická operace vytvoří výsledek, jehož velikost je pro formát příliš velkádecimal, System.OverflowException vyvolá se vyvolá.
Typ decimal má větší přesnost, ale může mít menší rozsah než typy s plovoucí desetinnou čárkou. Převody z typů s plovoucí desetinnou čárkou tak mohou vést k decimal výjimkám přetečení a převody z decimal typů s plovoucí desetinnou čárkou mohou způsobit ztrátu přesnosti nebo přetečení výjimek. Z těchto důvodů neexistují žádné implicitní převody mezi typy s plovoucí desetinou čárkou a decimalbez explicitního přetypování, dojde k chybě v době kompilace, když jsou plovoucí desetiny a decimal operandy přímo smíšené ve stejném výrazu.
8.3.9 Typ Bool
Typ bool představuje logické množství logických hodnot. Možné hodnoty typu bool jsou true a false. Vyjádření false je popsáno v §8.3.3. I když je vyjádření true neurčené, liší se od falsevyjádření .
Mezi a jinými typy hodnot neexistují bool žádné standardní převody. Konkrétně bool je typ odlišný a oddělený od integrálních typů, bool hodnotu nelze použít místo celočíselné hodnoty a naopak.
Poznámka: V jazycích C a C++ lze hodnotu nuly nebo hodnotu s plovoucí desetinou čárkou nebo ukazatel null převést na logickou hodnotu
falsea nenulovou celočíselnou hodnotu nebo hodnotu s plovoucí desetinou čárkou nebo nenulový ukazatel lze převést na logickou hodnotutrue. V jazyce C# se takové převody provádějí explicitním porovnáním celočíselné hodnoty nebo hodnoty s plovoucí desetinou čárkou na nulu nebo explicitním porovnáním odkazu nanullobjekt . koncová poznámka
8.3.10 – typy výčtu
Typ výčtu je jedinečný typ s pojmenovanými konstantami. Každý typ výčtu má základní typ, který musí být byte, sbyte, , short, , ushort, , nebo intuintlong . ulong Sada hodnot typu výčtu je stejná jako sada hodnot základního typu. Hodnoty typu výčtu nejsou omezeny na hodnoty pojmenovaných konstant. Typy výčtu jsou definovány prostřednictvím deklarací výčtu (§20.2).
8.3.11 Typy řazené kolekce členů
8.3.11.1 Obecné
Typ řazené kolekce členů představuje seřazenou sekvenci s pevnou délkou hodnot s volitelnými názvy a jednotlivými typy. Počet prvků v typu řazené kolekce členů se označuje jako jeho arity. Typ řazené kolekce členů je zapsán (T1 I1, ..., Tn In) s n ≥ 2, kde identifikátory jsou volitelné I1...In řazené kolekce členů.
Názvy prvků v rámci typu řazené kolekce členů musí být odlišné. Název prvku řazené kolekce členů formuláře ItemX, kde X je jakákoli posloupnost desetinných číslic bez počátečních nul, je povolena pouze na pozici označené .X
Poznámka Toto omezení názvů prvků zabraňuje nejasnostem mezi nimi a názvy polí řazené kolekce členů, např. pokud je název
ItemXprvku přidružen k poliItemY, kdeX ≠ Y. koncová poznámka
Poznámka Volitelné názvy prvků nejsou reprezentovány v reprezentaci modulu runtime (§8.3.11.3) hodnoty řazené kolekce členů.
Převody identit (§10.2.2) existují mezi řazenými kolekcemi členů se stejnou sekvencí typů prvků konvertibilními identitami.
Hodnoty řazené kolekce členů lze vytvořit z literálů řazené kolekce členů (§12.8.6) nebo vytvořením hodnoty pomocí podkladového vyjádření za běhu (§8.3.11.3). Syntaxi (T1, ..., Tn) typu řazené kolekce členů nelze použít s operátorem new§12.8.17.2.
Prvky řazené kolekce členů jsou veřejná pole s názvy Item1 ...
ItemN, kde N je řazená kolekce členů a čísla nemají žádné počáteční nuly a lze k němu získat přístup prostřednictvím přístupu člena v hodnotě řazené kolekce členů (§12.8.7. Kromě toho, pokud typ řazené kolekce členů má název pro daný prvek, lze tento název použít pro přístup k danému prvku.
Příklad: V následujících příkladech:
(int, string) pair1 = (1, "One"); (int, string word) pair2 = (2, "Two"); (int number, string word) pair3 = (3, "Three"); (int Item1, string Item2) pair4 = (4, "Four"); (int Item2, string Item123) pair5 = (5, "Five"); // Error: “Item” names do not match position Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}");Typy řazené kolekce členů pro
pair1,pair2apair3jsou všechny platné, s názvy pro ne, některé nebo všechny prvky typu řazené kolekce členů.Typ
pair4řazené kolekce členů je platný, protože názvyItem1aItem2odpovídají jejich pozicím, zatímco typ řazené kolekce členů jepair5zakázán, protože názvyItem2aItem123ne.Poslední řádek ukazuje, že k prvkům řazené kolekce členů může přistupovat
Itemnázev odpovídající jejich pozici, a také odpovídající název elementu řazené kolekce členů, pokud jsou přítomné v typu. konec příkladu
8.3.11.2 Posuvné zprostředkující vytváření řazené kolekce členů
Pokud výsledek vytvoření řazené kolekce členů (§12.8.6) není vyžadován mimo kontext, ve kterém je sestaven, pak implementace jsou explicitně povoleny k elidu konstrukce za předpokladu, že jsou splněny všechny ostatní sémantické požadavky.
Příklad: Taková situace může často vzniknout z dekonstrukcí přiřazení ($deconstructing přiřazení) a výpisů přepínače (§13.8.3). Zvažte dekonstrukci přiřazení:
(a, b) = (b, a);Pravá strana
(b, a)vytvoří řazenou kolekci členů obsahující hodnotyb&a. Levá strana dekonstruktor(a, b)pak v pořadí vybere první položku této řazené kolekce členů a přiřadí jia, následované přiřazením druhé položkyb. Celkovým výsledkem jsou hodnoty vyměněnéaabřazená kolekce členů vytvořená během tohoto procesu se zahodí. Explicitní příspěvek udělený zde k elide takové přechodné konstrukce řazené kolekce členů umožňuje implementaci vyměnit dvě hodnoty jakýmkoli způsobem, který zvolí, aby se vyhodnocujebpředasplněním pořadí hodnocení řazené kolekce členů literálů zleva doprava. V kódu:(a, b, _) = (b, a, thing.ExpensiveMethod(x));Implementace může také zvolit výměnu dvou hodnot bez vytvoření řazené kolekce členů za předpokladu, že prvky řazené kolekce členů jsou vyhodnoceny v pořadí:
baathing.ExpensiveMethod(x); před tím. end example
Poznámka: Pokud implementace elides přechodné řazené řazené kolekce členů může být také schopen elide nyní "redundant" (bez efektu) výrazy. Pokud je například průběžná řazená kolekce členů výsledkem implicitního převodu řazené kolekce členů, tyto implicitní převody nemají žádné vedlejší účinky a průběžná řazená kolekce členů podléhá dekonstrukci, kde jsou některé prvky zahozeny, může být možné hodit implicitní převod těchto zahozených prvků. koncová poznámka
8.3.11.3 Reprezentace za běhu
Poznámka: Na rozdíl od jiných typů, jako jsou pole, je reprezentace typů řazených kolekcí členů určena z hlediska sady obecných hodnot a řazená kolekce členů může být přímo odkazována z hlediska této reprezentace. Reprezentace těchto obecných typů hodnot však zůstává definovaná implementací. koncová poznámka
Reprezentace řazené kolekce (T1, ..., Tn) členů je vytvořena z System.ValueTuple<...> instancí (§C.3), které jsou sadou obecných typů struktur pro reprezentaci typů řazených kolekcí členů dvou až sedmi. Řazené kolekce členů s arity větší než sedm jsou reprezentovány obecným typem System.ValueTuple<T1, ..., T7, TRest> struktury, který kromě prvků řazené kolekce členů obsahuje Rest pole obsahující vnořené System.ValueTuple zbývající prvky. Pokud je požadováno pouze jedno další pole, použije se obecný typ System.ValueTuple<T1> struktury; tento typ se nepovažuje za samotný typ řazené kolekce členů. Pokud je vyžadováno System.ValueTuple<T1, ..., T7, TRest> více než sedm dalších polí, jsou další instance vnořené.
Příklad:
(T1, T2)je reprezentovánaValueTuple<T1, T2>
(T1, ..., T15)je reprezentovánaValueTuple<T1, ..., T7, ValueTuple<T8, ..., T14, ValueTuple<T15>>>konec příkladu
Reprezentace řazených kolekcí členů je přímo přístupná a řazené kolekce členů a System.ValueTuple<...> typy členů mohou být vzájemně zaměnitelné:
- Jakákoli hodnota typu
(T1, ..., Tn)může být považována za ekvivalentníSystem.ValueTuple<...>hodnotu. - Libovolná hodnota typu
System.ValueTuple<T1, T2>prostřednictvímSystem.ValueTuple<T1, T2, T3, T4, T5, T6, T7>hodnoty řazené kolekce členů může být považována za ekvivalent(T1, T2)(T1, T2, T3, T4, T5, T6, T7). - Hodnota typu
System.ValueTuple<T1, ..., T7, TRest>může být považována pouze za řazenou kolekci členů, pokudTRestje řazenou kolekcí členů nebo jakýmkoliSystem.ValueTuple<...>typem, včetněSystem.ValueTuple<T1>. - Jakákoli jiná hodnota typu
System.ValueTuple<T1>nemusí být považována za řazenou kolekci členů.
Jakýkoli pokus o použití System.ValueTuple<...> hodnoty jako řazené kolekce členů, která nesplňuje výše uvedené požadavky, je chyba v době kompilace.
Poznámka: K takové
System.ValueTuple<...>hodnotě je možné přistupovat pomocí veřejných členů, které poskytuje, stejně jako jakákoli jiná konstruovaná hodnota, nelze k ní získat přístup jako řazená kolekce členů. koncová poznámka
Příklad:
ValueTuples, které lze považovat za řazené kolekce členů (a&c) nebo ne (b):var a = new ValueTuple<int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7); var (a1, a2, a3, a4, a5, a6, a7) = a; // OK, a can be treated as a tuple var b = new ValueTuple<int, int, int, int, int, int, int, int> { Item1 = 1, Item2 = 2, Item3 = 3, Item4 = 4, Item5 = 5, Item6 = 6, Item7 = 7, Rest = 8 }; var b8 = b.Item8; // Error, b cannot be treated as an 8-tuple var c = new ValueTuple<int, int, int, int, int, int, int, ValueTuple<int>> (1, 2, 3, 4, 5, 6, 7, new ValueTuple<int>(8)); var c8 = c.Item8; // OK, c can be treated as a tuple and so has a field Item8konec příkladu
Příklad: Zaměnitelnost řazené kolekce členů a
ValueTuple:(int, string) pair6 = new ValueTuple<int, string>(6, "Six"); ValueTuple<int, string> pair7 = (7, "Seven");Prohlášení a
pair6pair7ukazují, že typy a výrazy řazené kolekce členů jsou obecně zaměnitelné sValueTuple<...>typy a výrazy vytváření objektů (§12.8.17.2).konec příkladu
Příklad: Pokud reprezentace řazené kolekce členů používá instance
System.ValueTuple<T1, ..., T7, TRest>,Restje pole přístupné. Použití tohoto příkazu poskytuje různé způsoby, jak odkazovat na položky ve velkých řazených kolekcích členů. Daný:var squares = (1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225);Pak lze 15. čtverec (
225) řešit jakosquares.Item15,squares.Rest.Item8asquares.Rest.Rest.Item1.konec příkladu
Ačkoli řazené kolekce členů a System.ValueTuple<...> hodnoty mohou být považovány za ekvivalentní, s uvedenými výše uvedenými hodnotami existuje důležitý sémantický rozdíl mezi řazenou kolekcí členů a System.ValueTuple<...> typy – pouze názvy dříve podporujících řazené kolekce členů (§8.3.11.1).
Příklad: Pouze syntaxe typu řazené kolekce členů podporuje názvy elementů. Vzhledem k tomu, že názvy jsou součástí typu kompilace a nikoli hodnoty, může zacházet s hodnotou typu
ValueTuple<...>jako řazenou kolekcí členů názvy elementů:var a = new ValueTuple<string, int>("Bert", 42); // Construct a ValueTuple (string name, int age) b = a; // Treat as a tuple with named elements Console.WriteLine($"{b.name} is {b.age} years old"); // Access using element nameskonec příkladu
Ve zbývající části tohoto standardu se zaměnitelnost řazených kolekcí členů a typů a ValueTuple<...> hodnot, jak je uvedeno výše, se obvykle bere jako přečtená a výslovně se o tom nezmíní.
8.3.12 Typy hodnot s možnou hodnotou Null
Typ hodnoty s možnou hodnotou null může představovat všechny hodnoty jeho základního typu a další hodnotu null. Typ hodnoty s možnou hodnotou null je zapsán T?, kde T je podkladový typ. Tato syntaxe je zkratka pro System.Nullable<T>a dvě formy lze zaměnitelně.
Naopak nenulový typ hodnoty je jakýkoli jiný typ hodnoty než System.Nullable<T> a jeho zkratka T? (pro jakýkoli T), plus jakýkoli parametr typu, který je omezen na typ hodnoty, který není nullable (tj. jakýkoli parametr typu s omezením typu hodnoty (§15.2.5)). Typ System.Nullable<T> určuje omezení typu hodnoty pro T, což znamená, že základní typ typu hodnoty null může být libovolný typ hodnoty, který není nullable. Základní typ typu hodnoty s možnou hodnotou null nemůže být typ hodnoty null nebo odkazový typ. Jedná se například int?? o neplatný typ. Odkazové typy s možnou hodnotou null jsou zahrnuty v §8.9.
Instance typu T? hodnoty null s možnou hodnotou má dvě veřejné vlastnosti jen pro čtení:
- Vlastnost
HasValuetypubool - Vlastnost
ValuetypuT
Příklad, pro který HasValue je true řečeno, že není null. Instance, která není null, obsahuje známou hodnotu a Value vrátí danou hodnotu.
Příklad, pro který HasValue je false řečeno, že má být null. Instance null má nedefinovanou hodnotu. Pokus o načtení Value instance null způsobí System.InvalidOperationException vyvolání. Proces přístupu k Value vlastnost nullable instance se označuje jako unwrapping.
Kromě výchozí konstruktoru má každý typ T? hodnoty nullable veřejný konstruktor s jedním parametrem typu T. Zadaná hodnota x typu T, konstruktor vyvolání formuláře
new T?(x)
vytvoří nenulovou instanci T? , pro kterou Value je xvlastnost . Proces vytvoření nenulové instance typu hodnoty s možnou hodnotou null pro danou hodnotu se označuje jako zabalení.
Implicitní převody jsou k dispozici z literálu na (§10.2.7null).T?
Typ T? hodnoty s možnou hodnotou null implementuje žádná rozhraní (§19). Konkrétně to znamená, že neimplementuje žádné rozhraní, které základní typ T dělá.
8.3.13 Boxing a unboxing
Koncept boxování a rozbalování poskytuje most mezi value_types a reference_types tím, že umožňuje převést libovolnou hodnotu value_type na typ a z typu object. Boxing a unboxing umožňuje jednotné zobrazení systému typů, kde hodnota libovolného typu může být nakonec považována objectza .
Balení je podrobněji popsáno v §10.2.9 a rozbalení je popsáno v §10.3.7.
8.4 Konstruované typy
8.4.1 Obecné
Deklarace obecného typu sama o sobě označuje nevázaný obecný typ , který se používá jako "podrobný plán" k vytvoření mnoha různých typů pomocí použití argumentů typu. Argumenty typu se zapisují v hranatých závorkách (< a >) bezprostředně za názvem obecného typu. Typ, který obsahuje alespoň jeden argument typu, se nazývá konstruovaný typ. Vytvořený typ lze použít na většině míst v jazyce, ve kterém se může zobrazit název typu. Nevázaný obecný typ lze použít pouze v rámci typeof_expression (§12.8.18).
Konstruované typy lze použít také ve výrazech jako jednoduché názvy (§12.8.4) nebo při přístupu ke členu (§12.8.7).
Při vyhodnocování namespace_or_type_name se považují pouze obecné typy se správným počtem parametrů typu. Proto je možné použít stejný identifikátor k identifikaci různých typů, pokud mají typy různá čísla parametrů typu. To je užitečné při kombinování obecných a ne generických tříd ve stejném programu.
Příklad:
namespace Widgets { class Queue {...} class Queue<TElement> {...} } namespace MyApplication { using Widgets; class X { Queue q1; // Non-generic Widgets.Queue Queue<int> q2; // Generic Widgets.Queue } }konec příkladu
Podrobná pravidla pro vyhledávání názvů v namespace_or_type_name produkce jsou popsána v §7.8. Řešení nejednoznačností v těchto výrobách je popsáno v §6.2.5.
Type_name může identifikovat vytvořený typ, i když přímo nezadá parametry typu. K tomu může dojít v případě, že je typ vnořený do obecné class deklarace a typ instance obsahující deklarace se implicitně používá pro vyhledávání názvů (§15.3.9.7).
Příklad:
class Outer<T> { public class Inner {...} public Inner i; // Type of i is Outer<T>.Inner }konec příkladu
Nevyčtový typ nesmí být použit jako unmanaged_type (§8.8).
8.4.2 Argumenty typu
Každý argument v seznamu argumentů typu je jednoduše typ.
type_argument_list
: '<' type_argument (',' type_argument)* '>'
;
type_argument
: type
| type_parameter nullable_type_annotation?
;
Každý argument typu musí splňovat všechna omezení odpovídajícího parametru typu (§15.2.5). Argument typu odkazu, jehož hodnota nullability neodpovídá možnosti null parametru typu, splňuje omezení; však může být vydáno upozornění.
8.4.3 Otevřené a uzavřené typy
Typ je otevřený nebouzavřený typ. Otevřený typ je typ, který zahrnuje parametry typu. Konkrétně:
- Parametr typu definuje otevřený typ.
- Typ pole je otevřený typ, pokud je jeho typ prvku otevřený typ.
- Vytvořený typ je otevřený typ, pokud jeden nebo více argumentů typu je otevřený typ. Konstruovaný vnořený typ je otevřený typ, pouze pokud jeden nebo více argumentů typu nebo argumenty typu jednoho nebo více jeho obsahujících typů je otevřený typ.
Uzavřený typ je typ, který není otevřeným typem.
Za běhu se veškerý kód v deklaraci obecného typu spustí v kontextu uzavřeného konstruovaného typu, který byl vytvořen použitím argumentů typu na obecnou deklaraci. Každý parametr typu v rámci obecného typu je vázán na konkrétní typ běhu. Zpracování všech příkazů a výrazů za běhu vždy probíhá u uzavřených typů a otevřené typy probíhají pouze během zpracování v době kompilace.
Dva uzavřené konstruované typy jsou konvertibilní identity (§10.2.2), pokud jsou sestaveny ze stejného nevázaného obecného typu a mezi každým z jejich odpovídajících argumentů typu existuje převod identity. Odpovídající argumenty typu mohou být samy o sobě uzavřeny vytvořené typy nebo řazené kolekce členů, které jsou konvertibilními identitami. Uzavřené konstruované typy, které jsou konvertibilními identitami, sdílejí jednu sadu statických proměnných. V opačném případě má každý uzavřený vytvořený typ vlastní sadu statických proměnných. Vzhledem k tomu, že otevřený typ neexistuje za běhu, neexistují žádné statické proměnné přidružené k otevřenému typu.
8.4.4 Vázané a nevázané typy
Nevázaný typ odkazuje na ne generický typ nebo nevázaný obecný typ. Vázaný typ termínu odkazuje na ne generický typ nebo konstruovaný typ.
Nevázaný typ odkazuje na entitu deklarovanou deklarací typu. Nevázaný obecný typ není sám o sobě typ a nelze ho použít jako typ proměnné, argumentu nebo návratové hodnoty nebo jako základní typ. Jediným konstruktem, ve kterém lze odkazovat na nevázaný obecný typ, je typeof výraz (§12.8.18).
8.4.5 Vyhovující omezení
Při každém odkazování na vytvořený typ nebo obecnou metodu jsou zadané argumenty typu kontrolovány proti omezením parametru typu deklarovaným u obecného typu nebo metody (§15.2.5). Pro každou where klauzuli je argument A typu, který odpovídá parametru pojmenovaného typu, kontrolován proti každému omezení následujícím způsobem:
- Pokud je omezení typem, typem
classrozhraní nebo parametrem typu, představteCtoto omezení zadanými argumenty typu nahrazenými všemi parametry typu, které se zobrazí v omezení. Aby bylo toto omezení splněno, musí se jednat o případ, kdy je typ konvertibilní na typACjedním z následujících způsobů: - Je-li toto omezení omezením referenčního typu (
class), musí typAsplňovat jednu z těchto možností:-
Aje typ rozhraní, typ třídy, typ delegáta, typ pole nebo dynamický typ.
Poznámka:
System.ValueTypeASystem.Enumjsou odkazové typy, které splňují toto omezení. koncová poznámka-
Aje parametr typu, který je znám jako referenční typ (§8.2).
-
- Je-li omezení omezením typu hodnoty (
struct), musí typAsplňovat jednu z následujících možností:-
Astructje typ neboenumtyp, ale ne typ hodnoty null.
Poznámka:
System.ValueTypeASystem.Enumjsou odkazové typy, které nevyhovují tomuto omezení. koncová poznámka-
Aje parametr typu s omezením typu hodnoty (§15.2.5).
-
- Je-li omezení konstruktoru omezení
new(), typAnesmí býtabstracta musí mít veřejný konstruktor bez parametrů. To je splněné, pokud platí jedna z následujících možností:-
Aje typ hodnoty, protože všechny typy hodnot mají veřejný výchozí konstruktor (§8.3.3). -
Aje parametr typu s omezením konstruktoru (§15.2.5). -
Aje parametr typu s omezením typu hodnoty (§15.2.5). -
Aclassje objekt, který není abstraktní a obsahuje explicitně deklarovaný veřejný konstruktor bez parametrů. -
Aneníabstracta má výchozí konstruktor (§15.11.5).
-
K chybě v době kompilace dochází, pokud některý z omezení parametru typu není splněn zadanými argumenty typu.
Vzhledem k tomu, že parametry typu nejsou zděděné, omezení se nikdy nedědí.
Příklad: V následujícím příkladu je nutné zadat omezení parametru jeho typu
Dtak,TabyTsplňovalo omezení stanovené základemclassB<T>. Naproti tomu není nutné specifikovat omezení,classEprotožeList<T>implementujeIEnumerablepro libovolnouT.class B<T> where T: IEnumerable {...} class D<T> : B<T> where T: IEnumerable {...} class E<T> : B<List<T>> {...}konec příkladu
Parametry typu 8.5
Parametr typu je identifikátor označující typ hodnoty nebo odkazový typ, ke kterému je parametr vázán za běhu.
type_parameter
: identifier
;
Vzhledem k tomu, že parametr typu lze vytvořit instanci s mnoha různými argumenty typu, parametry typu mají mírně odlišné operace a omezení než jiné typy.
Poznámka: Patří mezi ně:
- Parametr typu nelze použít přímo k deklaraci základní třídy (§15.2.4.2) nebo rozhraní (§19.2.4).
- Pravidla pro vyhledávání členů na parametrech typu závisí na omezeních použitých u parametru typu. Jsou podrobně popsány v §12.5.
- Dostupné převody parametru typu závisí na omezeních použitých u parametru typu. Jsou podrobně popsány v §10.2.12 a §10.3.8.
- Literál
nullnelze převést na typ zadaný parametrem typu, s výjimkou případů, kdy je parametr typu znám jako referenční typ (§10.2.12). Místo toho lze použít výchozí výraz (§12.8.21). Kromě toho lze hodnotu s typem zadaným parametrem typu porovnat s hodnotou null pomocí a==!=(§12.15.7), pokud parametr typu nemá omezení typu.- Výraz
new(§12.8.17.2) lze použít pouze s parametrem typu, pokud je parametr typu omezen constructor_constraint nebo omezením typu hodnoty (§15.2.5).- Parametr typu nelze použít kdekoli v atributu (§23.2.1).
- Parametr typu nelze použít v přístupu ke členu (§12.8.7) nebo název typu (§7.8) k identifikaci statického členu nebo vnořeného typu.
- Parametr typu lze použít pouze jako unmanaged_type (§8.8), pokud je parametr typu omezen nespravovaným omezením (§15.2.5).
- Poznámky s možnou hodnotou null (
?) nejsou povoleny u instance parametru typu, pokud není tento parametr typu omezen na odkazový typ nebo typ hodnoty (§15.2.5).koncová poznámka
Jako typ jsou parametry typu čistě konstruktorem kompilačního času. Za běhu je každý parametr typu svázán s typem za běhu, který byl určen zadáním argumentu typu deklaraci obecného typu. Proto typ proměnné deklarované s parametrem typu bude v době běhu uzavřený konstruovaný typ §8.4.3. Spuštění všech příkazů a výrazů zahrnujících parametry typu používá typ, který byl zadán jako argument typu pro tento parametr.
8.6 Typy stromu výrazů
Strom výrazů umožňuje, aby byl výraz lambda reprezentován jako datová struktura místo spustitelného kódu. Stromy výrazů je hodnota stromu výrazu typu formuláře System.Linq.Expressions.Expression<TDelegate>, kde TDelegate je jakýkoli typ delegáta. Ve zbývající části této specifikace se tyto typy budou odkazovat pomocí zkratky Expression<TDelegate>.
Pokud převod existuje z výrazu lambda na typ Ddelegáta , převod existuje také na typ Expression<TDelegate>stromu výrazu . Zatímco převod výrazu lambda na typ delegáta generuje delegáta, který odkazuje na spustitelný kód výrazu lambda, převod na typ stromu výrazu vytvoří strom výrazu reprezentaci výrazu lambda výrazu. Další podrobnosti o převodu jsou uvedeny v §10.7.3.
Příklad: Následující program představuje výraz lambda jak jako spustitelný kód, tak jako strom výrazu. Vzhledem k tomu, že převod existuje na
Func<int,int>:Expression<Func<int,int>>Func<int,int> del = x => x + 1; // Code Expression<Func<int,int>> exp = x => x + 1; // DataPo těchto přiřazeních delegát
delodkazuje na metodu, která vracíx + 1, a strom výrazu exp odkazuje na datovou strukturu, která popisuje výrazx => x + 1.konec příkladu
Expression<TDelegate> poskytuje metodu Compile instance, která vytváří delegáta typu TDelegate:
Func<int,int> del2 = exp.Compile();
Vyvolání tohoto delegáta způsobí spuštění kódu reprezentovaného stromem výrazu. Vzhledem k výše uvedeným del definicám a del2 jsou ekvivalentní a následující dva příkazy budou mít stejný účinek:
int i1 = del(1);
int i2 = del2(1);
Po spuštění tohoto kódu i1 a i2 oba budou mít hodnotu 2.
Povrch rozhraní API poskytovaný Expression<TDelegate> implementací je definován nad rámec požadavku na metodu popsanou Compile výše.
Poznámka: Přestože jsou podrobnosti rozhraní API poskytované pro stromy výrazů definované implementací, očekává se, že implementace:
- Povolení kódu ke kontrole struktury stromu výrazů vytvořeného v důsledku převodu výrazu z výrazu lambda a reakce na ni
- Povolení programového vytváření stromů výrazů v uživatelském kódu
koncová poznámka
8.7 Dynamický typ
Typ dynamic používá dynamickou vazbu, jak je podrobně popsáno v §12.3.2, na rozdíl od statické vazby, kterou používají všechny ostatní typy.
dynamic Typ se považuje za identický s object výjimkou následujících hledisek:
- Operace s výrazy typu
dynamiclze dynamicky svázat (§12.3.3). - Odvození typu (§12.6.3) bude preferovat
dynamic,objectpokud jsou oba kandidáti. -
dynamicnelze použít jako- typ v object_creation_expression (§12.8.17.2)
- a class_base (§15.2.4)
- predefined_type v member_access (§12.8.7.1)
- operand operátoru
typeof - argument atributu
- omezení
- typ metody rozšíření
- část argumentu typu v rámci struct_interfaces (§16.2.5) nebo interface_type_list (§15.2.4.1).
Z důvodu této ekvivalence platí následující:
- Existuje implicitní převod identity.
- mezi a
objectdynamic - mezi konstruované typy, které jsou stejné při nahrazení
dynamicobject - mezi typy řazené kolekce členů, které jsou stejné při nahrazení
dynamicobject
- mezi a
- Implicitní a explicitní převody na a z
objecttaké platí pro a zdynamic. - Podpisy, které jsou stejné při nahrazování
dynamicobject, se považují za stejný podpis. -
dynamicTyp je nerozlišitelný od typuobjectza běhu. - Výraz typu
dynamicse označuje jako dynamický výraz.
8.8 Nespravované typy
unmanaged_type
: value_type
| pointer_type // unsafe code support
;
unmanaged_type je jakýkoli typ, který není reference_type ani type_parameter, který není omezen na to, že musí být nespravovaný, a neobsahuje žádná instance pole, jejichž typ není unmanaged_type. Jinými slovy, unmanaged_type je jedním z následujících způsobů:
-
sbyte,byte, ,short,ushort,intuintnintnuintlongulongcharfloatdoubledecimalnebo .bool - Všechny enum_type.
- Všechny uživatelem definované struct_type, které obsahují pouze pole instance unmanaged_types.
- Libovolný parametr typu, který je omezen na nespravovaný.
- Jakékoli pointer_type (§24.3).
8.9 Odkazové typy a možnosti null
8.9.1 Obecné
Typ odkazu s možnou hodnotou null je označen připojením nullable_type_annotation (?) k nenulovatelnému typu odkazu. Neexistuje žádný sémantický rozdíl mezi nenulovým odkazovatelným typem a odpovídajícím typem nullable, oba mohou být odkazem na objekt nebo null. Přítomnost nebo absence nullable_type_annotation deklaruje, zda má výraz povolit hodnoty null, nebo ne. Kompilátor může poskytnout diagnostiku, pokud se výraz nepoužívá v souladu s tímto záměrem. Stav null výrazu je definován v §8.9.5. Převod identity existuje mezi referenčním typem s možnou hodnotou null a jeho odpovídajícím odkazovým typem, který není nullable (§10.2.2).
Existují dvě formy nullability pro odkazové typy:
- nullable: Lze . Výchozí stav null je možná null.
-
non-nullable: Odkaz s možnou
nullhodnotou null by neměl být přiřazen. Výchozí stav null není null.
Poznámka: Typy
RaR?jsou reprezentovány stejným základním typem,R. Proměnná tohoto základního typu může obsahovat odkaz na objekt nebo být hodnotounull, která označuje "žádný odkaz". koncová poznámka
Syntaktické rozlišení mezi typem odkazu s možnou hodnotou null a odpovídajícím typem odkazu, který není nullable, umožňuje kompilátoru generovat diagnostiku. Kompilátor musí povolit nullable_type_annotation , jak je definováno v §8.2.1. Diagnostika musí být omezená na upozornění. Přítomnost ani absence poznámek s možnou hodnotou null ani stav kontextu s možnou hodnotou null nemůže změnit čas kompilace nebo chování programu s výjimkou změn v diagnostických zprávách vygenerovaných v době kompilace.
8.9.2 Odkazové typy bez hodnoty null
Typ odkazu , který není nullable, je odkaz typu formuláře T, kde T je název typu. Výchozí stav null proměnné bez hodnoty null není null. Upozornění se můžou generovat, když se použije výraz, který je možná null , kde se vyžaduje hodnota not-null .
8.9.3 Odkazové typy s možnou hodnotou Null
Typ odkazu ve formuláři (například T?) je odkazový typstring?hodnotou null. Výchozí stav null proměnné s možnou hodnotou null je možná null. Poznámka ? označuje záměr, že proměnné tohoto typu mají hodnotu null. Kompilátor dokáže tyto záměry rozpoznat, aby vydával upozornění. Pokud je kontext poznámek s možnou hodnotou null zakázaný, může použití této poznámky vygenerovat upozornění.
8.9.4 Kontext s možnou hodnotou Null
8.9.4.1 Obecné
Každý řádek zdrojového kódu má kontext s možnou hodnotou null. Poznámky a upozornění označují příznaky nullable kontextových poznámek s možnou hodnotou null (§8.9.4.3) a upozornění s možnou hodnotou null (§8.9.4.4). Každý příznak může být povolený nebo zakázaný. Kompilátor může použít statickou analýzu toku k určení stavu null jakékoli referenční proměnné. Stav null referenční proměnné (§8.9.5) buď není null, možná null nebo možná výchozí.
Kontext s možnou hodnotou null lze zadat ve zdrojovém kódu prostřednictvím direktiv s možnou hodnotou null (§6.5.9) nebo prostřednictvím některého mechanismu specifického pro implementaci externího zdrojového kódu. Pokud se používají oba přístupy, direktivy s možnou hodnotou null nahrazují nastavení provedená externím mechanismem.
Výchozí stav kontextu s možnou hodnotou null je definována implementace.
V této specifikaci se předpokládá, že byl zkompilován veškerý kód jazyka C#, který neobsahuje direktivy s možnou hodnotou null nebo o kterém není žádný příkaz týkající se aktuálního stavu kontextu s možnou hodnotou null zkompilován pomocí kontextu s možnou hodnotou null, kde jsou povoleny poznámky i upozornění.
Poznámka: Kontext s možnou hodnotou null, kde jsou oba příznaky zakázány, odpovídá předchozímu standardnímu chování pro odkazové typy. koncová poznámka
8.9.4.2 Zákaz s možnou hodnotou Null
Pokud jsou příznaky upozornění i poznámek zakázané, kontext s možnou hodnotou null je zakázán.
Pokud je kontext s možnou hodnotou null zakázán:
- Pokud je proměnná neoznačeného referenčního typu inicializována nebo přiřazena hodnotou ,
nullnesmí být generována žádná upozornění . - Pokud proměnná referenčního typu, která může mít hodnotu null, se nevygeneruje žádné upozornění.
- Pro jakýkoli odkaz typ
T, poznámka?vygenerujeT?zprávu a typT?je stejný jakoT. - Pro jakékoli omezení
where T : C?parametru typu, anotace?vygenerujeC?zprávu a typC?je stejný jakoC. - Pro jakékoli omezení
where T : U?parametru typu, anotace?vygenerujeU?zprávu a typU?je stejný jakoU. - Obecné omezení
class?vygeneruje zprávu upozornění. Parametr typu musí být referenčním typem.Poznámka: Tato zpráva je charakterizována jako "informační" místo "varování", takže ji nezaměňovat se stavem nastavení upozornění s možnou hodnotou null, což nesouvisí. koncová poznámka
- Operátor
!zrušení zrušení (§12.8.9) nemá žádný účinek.
Příklad:
#nullable disable annotations string? s1 = null; // Informational message; ? is ignored string s2 = null; // OK; null initialization of a reference s2 = null; // OK; null assignment to a reference char c1 = s2[1]; // OK; no warning on dereference of a possible null; // throws NullReferenceException c1 = s2![1]; // OK; ! is ignoredkonec příkladu
8.9.4.3 Poznámky s možnou hodnotou Null
Pokud je příznak upozornění zakázaný a příznak poznámek je povolený, kontext s možnou hodnotou null je poznámky.
Pokud je kontext s možnou hodnotou null poznámky:
- U libovolného typu odkazu
Toznačuje poznámka?typuT?T?s možnou hodnotou null, zatímco neoznačenéTnení nullable. - Negenerují se žádná diagnostická upozornění týkající se možnosti null.
- Operátor
!null-forgiving (§12.8.9) může změnit analyzovaný stav null svého operandu a jaká jsou varovná upozornění diagnostiky času kompilace.
Příklad:
#nullable disable warnings #nullable enable annotations string? s1 = null; // OK; ? makes s2 nullable string s2 = null; // OK; warnings are disabled s2 = null; // OK; warnings are disabled char c1 = s2[1]; // OK; warnings are disabled; throws NullReferenceException c1 = s2![1]; // No warningskonec příkladu
8.9.4.4 Upozornění s možnou hodnotou null
Pokud je příznak upozornění povolený a příznak poznámek je zakázaný, kontext s možnou hodnotou null je upozornění.
Pokud je kontext s možnou hodnotou null upozornění, může kompilátor vygenerovat diagnostiku v následujících případech:
- Referenční proměnná, která byla určena jako null, je dereferenced.
- Referenční proměnná nenulového typu je přiřazena výrazu, který může být null.
- Slouží
?k poznámce typu odkazu s možnou hodnotou null. - Operátor null-forgiving
!(§12.8.9) slouží k nastavení stavu null jeho operandu na hodnotu null.
Příklad:
#nullable disable annotations #nullable enable warnings string? s1 = null; // OK; ? makes s2 nullable string s2 = null; // OK; null-state of s2 is "maybe null" s2 = null; // OK; null-state of s2 is "maybe null" char c1 = s2[1]; // Warning; dereference of a possible null; // throws NullReferenceException c1 = s2![1]; // The warning is suppressedkonec příkladu
8.9.4.5 Povolení s možnou hodnotou Null
Pokud je povolen příznak upozornění i příznak poznámek, je povolený kontext s možnou hodnotou null.
Pokud je povolený kontext s možnou hodnotou null:
- U libovolného typu
Todkazu vytvoří?poznámkaT?typuT?s možnou hodnotou null, zatímco neoznačenéTje nenulové. - Kompilátor může použít statickou analýzu toku k určení stavu null jakékoli referenční proměnné. Pokud jsou povolena upozornění s možnou hodnotou null, stav null referenční proměnné (§8.9.5) buď není null, možná null, nebo možná výchozí a
- Operátor null-forgiving (
!) nastaví stav null svého operandu na hodnotu null. - Kompilátor může vydat upozornění, pokud se hodnota null parametru typu neshoduje s možnou hodnotou null odpovídajícího argumentu typu.
8.9.5 Nullabilities a null states
8.9.5.1 Obecné
Kompilátor není nutný k provádění žádné statické analýzy ani není vyžadován k vygenerování diagnostických upozornění souvisejících s nulovostí.
Zbytek tohoto dílčího seznamu je podmíněně normativní.
8.9.5.2 Analýza toku
Kompilátor, který generuje diagnostická upozornění, odpovídá těmto pravidlům.
Každý výraz má jeden ze tří stavůnull:
- možná null: Hodnota výrazu se může vyhodnotit na hodnotu null.
- možná výchozí hodnota: Hodnota výrazu se může vyhodnotit na výchozí hodnotu daného typu.
- not null: Hodnota výrazu není null.
Výchozí stav null výrazu je určen jeho typem a stav příznaku poznámek při deklaraci:
- Výchozí stav literálu
null(§6.4.5.7) je možná null. - Výchozí stav výchozího výrazu hodnoty (§12.8.21) není null, pokud je typ nenulovou hodnotou, jinak může být null.
- Pro všechny ostatní výrazy:
- Výchozí stav null výrazu, jehož typ je typu odkazu s možnou hodnotou null, je:
- Pokud je deklarace v textu, kde je příznak poznámek povolený, může mít hodnotu null.
- Není null, pokud je deklarace v textu, kde je příznak poznámek zakázán.
- Výchozí stav null výrazu, jehož typ je nenulový odkazový typ, není null.
- Výchozí stav null výrazu, jehož typ je typu odkazu s možnou hodnotou null, je:
Poznámka: Možná výchozí stav se používá s nekonstruovanými parametry typu, pokud je typ nenulový, například
stringa výrazdefault(T)je hodnotou null. Protože hodnota null není v doméně pro nenulový typ, je stav možná výchozí. koncová poznámka
Diagnostiku lze vytvořit, pokud je proměnná (§9.2.1) nenulového odkazu inicializována nebo přiřazena výrazu, který může mít hodnotu null, pokud je tato proměnná deklarována v textu, kde je povolen příznak poznámky.
Příklad: Vezměte v úvahu následující metodu, kde parametr má hodnotu null a tato hodnota je přiřazena k nenulovatelnému typu:
#nullable enable public class C { public void M(string? p) { // Warning: Assignment of maybe null value to non-nullable variable string s = p; } }
Kompilátor může vystavit upozornění, kdy parametr, který může mít hodnotu null, je přiřazen k proměnné, která by neměla mít hodnotu null. Pokud je parametr před přiřazením kontrolovaný hodnotou null, může kompilátor použít tento parametr v analýze stavu s možnou hodnotou null a nevystaví upozornění:
#nullable enable public class C { public void M(string? p) { if (p != null) { string s = p; // No warning // Use s } } }konec příkladu
Kompilátor může aktualizovat stav null proměnné jako součást analýzy.
Příklad: Kompilátor se může rozhodnout aktualizovat stav na základě libovolných příkazů v programu:
#nullable enable public void M(string? p) { int length = p.Length; // Warning: p is maybe null string s = p; // No warning. p is not null if (s != null) { int l2 = s.Length; // No warning. s is not null } int l3 = s.Length; // Warning. s is maybe null }V předchozím příkladu může kompilátor rozhodnout, že po příkazu
int length = p.Length;je nulový stavpnenulový. Pokud by byl null, tento příkaz by vyvolán .NullReferenceExceptionToto chování se podobá chování v případě, že kód byl předcházet s tím rozdílemif (p == null) throw NullReferenceException();, že kód, jak je zapsáno, může vést k upozornění, jehož účelem je upozornit, že výjimka může být vyvolána implicitně. konec příkladu
Později v metodě kód zkontroluje, že s není odkaz null. Stav s null se může po zavření bloku s kontrolou null změnit na hodnotu null. Kompilátor může odvodit, že s je možná null, protože kód byl zapsán tak, aby předpokládal, že mohl mít hodnotu null. Obecně platí, že pokud kód obsahuje kontrolu null, kompilátor může odvodit, že hodnota může mít hodnotu null:
Příklad: Každý z následujících výrazů obsahuje určitou formu kontroly null. Stav
ose může po každém z těchto příkazů změnit z není null na možná null.#nullable enable public void M(string s) { int length = s.Length; // No warning. s is not null _ = s == null; // Null check by testing equality. The null state of s // is maybe null length = s.Length; // Warning, and changes the null state of s // to not null _ = s?.Length; // The ?. is a null check and changes the null state of s // to maybe null if (s.Length > 4) // Warning. Changes null state of s to not null { _ = s?[4]; // ?[] is a null check and changes the null state of s // to maybe null _ = s.Length; // Warning. s is maybe null } }
Deklarace automatických vlastností i událostí podobných polím využívají interní pole generované kompilátorem. Analýza stavu null může odvodit, že přiřazení události nebo vlastnosti je přiřazením do kompilátorem generovaného podpůrného pole.
Příklad: Kompilátor může určit, že zápis do automatické vlastnosti nebo události podobné poli zapisuje do odpovídajícího kompilátorem generovaného podpůrného pole. Stav null vlastnosti odpovídá stavu podpěrného pole.
class Test { public string P { get; set; } public Test() {} // Warning. "P" not set to a non-null value. static void Main() { var t = new Test(); int len = t.P.Length; // No warning. Null state is not null. } }V předchozím příkladu konstruktor nenastaví
Phodnotu null a kompilátor může vydat upozornění. Při přístupu kPvlastnosti není žádné upozornění, protože typ vlastnosti je nenulový odkazový typ. konec příkladu
Kompilátor může zacházet s vlastností (§15.7) buď jako proměnná se stavem, nebo jako nezávislé získání a nastavení přístupových objektů (§15.7.3).
Příklad: Kompilátor může zvolit, zda zápis do vlastnosti změní stav null čtené vlastnosti, nebo zda čtení vlastnosti změní stav null této vlastnosti.
class Test { private string? _field; public string? DisappearingProperty { get { string tmp = _field; _field = null; return tmp; } set { _field = value; } } static void Main() { var t = new Test(); if (t.DisappearingProperty != null) { int len = t.DisappearingProperty.Length; // No warning. A compiler can // assume property is stateful } } }V předchozím příkladu je backingové pole pro pole
DisappearingPropertynastaveno na hodnotu null při čtení. Kompilátor však může předpokládat, že čtení vlastnosti nezmění stav null tohoto výrazu. konec příkladu
Kompilátor může použít libovolný výraz, který dereferencuje proměnnou, vlastnost nebo událost, aby nastavil stav null na stav nenulový. Pokud by byl null, výraz dereference by vyvolá NullReferenceException:
Příklad:
public class C { private C? child; public void M() { _ = child.child.child; // Warning. Dereference possible null value var greatGrandChild = child.child.child; // No warning. } }konec příkladu
8.9.5.3 Převody typů
Kompilátor, který generuje diagnostická upozornění, odpovídá těmto pravidlům.
Poznámka: Rozdíly v anotacích nulovatelnosti na nejvyšší úrovni nebo vnořených v typech nemají vliv na to, zda je povolen převod mezi typy, protože neexistuje žádný sémantický rozdíl mezi nepovinným odkazovým typem a jeho odpovídajícím nulovatelným typem (§8.9.1). koncová poznámka
Kompilátor může vydat varování, když se anotace nulovatelnosti liší mezi dvěma typy, a to buď na nejvyšší úrovni, nebo vnořenými, při zužování konverze.
Příklad: Typy se liší v poznámkách nejvyšší úrovně.
#nullable enable public class C { public void M1(string p) { _ = (string?)p; // No warning, widening } public void M2(string? p) { _ = (string)p; // Warning, narrowing _ = (string)p!; // No warning, suppressed } }konec příkladu
Příklad: Typy se liší v anotacích vnořeného nulovatelného typu.
#nullable enable public class C { public void M1((string, string) p) { _ = ((string?, string?))p; // No warning, widening } public void M2((string?, string?) p) { _ = ((string, string))p; // Warning, narrowing _ = ((string, string))p!; // No warning, suppressed } }konec příkladu
Kompilátor může dodržovat pravidla pro odchylku rozhraní (§19.2.3.3), odchylku delegování (§21.4) a maticovou kovarianci (§17.6) při určování, zda se má vydat upozornění na převody typů.
#nullable enable public class C { public void M1(IEnumerable<string> p) { IEnumerable<string?> v1 = p; // No warning } public void M2(IEnumerable<string?> p) { IEnumerable<string> v1 = p; // Warning IEnumerable<string> v2 = p!; // No warning } public void M3(Action<string?> p) { Action<string> v1 = p; // No warning } public void M4(Action<string> p) { Action<string?> v1 = p; // Warning Action<string?> v2 = p!; // No warning } public void M5(string[] p) { string?[] v1 = p; // No warning } public void M6(string?[] p) { string[] v1 = p; // Warning string[] v2 = p!; // No warning } }konec příkladu
Kompilátor může vydat upozornění, pokud se nulovatelnost liší v kterémkoli směru u typů, které nepovolují variantní konverzi.
#nullable enable public class C { public void M1(List<string> p) { List<string?> v1 = p; // Warning List<string?> v2 = p!; // No warning } public void M2(List<string?> p) { List<string> v1 = p; // Warning List<string> v2 = p!; // No warning } }konec příkladu
Kompilátor může vydat upozornění, pokud je výsledkem operátoru zvednutí převodu (§10.6.2) odkazový typ a převede se na nenulový odkazový typ, protože výsledek operátoru lifted conversion je možná null.
#nullable enable class C { void M() { C? a = (int?)null; // No warning C b = (int?)null; // Warning: Possible assignment to null C c = 1; // No warning } public static implicit operator C(int v) => new C(); }konec příkladu
Konec podmíněného normativního textu
ECMA C# draft specification