8 typů
8.1 Obecné
Typy jazyka C# jsou rozdělené do dvou hlavních kategorií: odkazové typy a typy hodnot. Typy hodnot i odkazové typy mohou být obecné typy, které přebírají 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 (§23.3) je k dispozici pouze v nebezpečném kódu (§23).
Typy hodnot se liší od referenčních typů v tom, že proměnné hodnotových typů přímo obsahují jejich data, zatímco proměnné referenčních typů ukládají odkazy na jejich data, druhá se označuje jako objekty. 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
nebo typ. 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 je k dispozici pouze v nebezpečném kódu (§23.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 §19.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 §20.1. |
System.Exception |
Základní třída všech typů výjimek. Viz §21.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 dynamic
se 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 §18.
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 §20.
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.
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'
| '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
;
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 class
System.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 implicitně deklarují veřejný konstruktor instance bez parametrů, který se nazývá výchozí konstruktor. 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
,int
,uint
,long
aulong
, výchozí hodnota je0
. - 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á0m
hodnota nula s měřítkem 0). - Pro
bool
, výchozí hodnota jefalse
. -
Pro enum_type
E
je0
vý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
HasValue
je vlastnost false. Výchozí hodnota se označuje také jako hodnota null typu hodnoty s možnou hodnotou null. Pokus o přečteníValue
vlastnosti 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. Pro typy hodnot vytvoří výchozí výraz hodnoty (§12.8.21) stejný výsledek jako při použití výchozího konstruktoru. koncová poznámka
Příklad: V následujícím kódu jsou proměnné
i
j
ak
všechny inicializovány na nulu.class A { void F() { int i = 0; int j = new int(); int k = default(int); } }
end example
Vzhledem k tomu, že každý typ hodnoty implicitně má veřejný konstruktor instance bez parametrů, není možné, aby typ struktury obsahoval explicitní deklaraci konstruktoru bez parametrů. Typ struktury je však povolen deklarovat parametrizované konstruktory instance (§16.4.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
Jazyk C# poskytuje sadu předdefinovaných typů označovaných struct
jako jednoduché typy. Jednoduché typy jsou identifikovány prostřednictvím klíčových slov, ale tato klíčová slova jsou jednoduše aliasy pro předdefinované struct
typy v System
oboru názvů, jak je popsáno v tabulce níže.
Klíčové slovo | Typ aliasu |
---|---|
sbyte |
System.SByte |
byte |
System.Byte |
short |
System.Int16 |
ushort |
System.UInt16 |
int |
System.Int32 |
uint |
System.UInt32 |
long |
System.Int64 |
ulong |
System.UInt64 |
char |
System.Char |
float |
System.Single |
double |
System.Double |
bool |
System.Boolean |
decimal |
System.Decimal |
Vzhledem k tomu, že jednoduchý typ typu představuje typ struktury, má každý jednoduchý typ členy.
Příklad:
int
má členy deklarované vSystem.Int32
a členy zděděné zSystem.Object
a 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 method
end example
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:
123
je literál typuint
a'a'
je literál typuchar
. end example- 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.23). Výrazy zahrnující operátory definované jinými typy struktur se nepovažují za konstantní výrazy.
- Prostřednictvím
const
deklarací 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.
8.3.6 Integrální typy
Jazyk C# podporuje devět integrálních typů: sbyte
, byte
, short
, ushort
int
, uint
, long
, ulong
, a char
. Integrální typy mají následující velikosti a rozsahy hodnot:
- Typ
sbyte
představuje 8bitové celé číslo se signem s hodnotami od-128
do127
včetně. - Typ
byte
představuje 8bitové celé číslo bez znaménka s hodnotami od0
do255
včetně. - Typ
short
představuje 16bitové celé číslo se signedms a hodnotami od-32768
do32767
včetně. - Typ
ushort
představuje 16bitové celé číslo bez znaménka s hodnotami od0
do65535
včetně. - Typ
int
představuje 32bitové celé číslo s hodnotami od-2147483648
do2147483647
včetně. - Typ
uint
představuje 32bitové celé číslo bez znaménka s hodnotami od0
do4294967295
včetně. - Typ
long
představuje 64bitové celé číslo se 64bitovými hodnotami od-9223372036854775808
do9223372036854775807
včetně. - Typ
ulong
představuje 64bitové celé číslo bez znaménka s hodnotami od0
do18446744073709551615
včetně. - Typ
char
představuje 16bitové celé číslo bez znaménka s hodnotami od0
do65535
včetně. Sada možných hodnot prochar
typ odpovídá znakové sadě Unicode.Poznámka: Ačkoli
char
má 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 32bitovou přesností, bez znaménka 32bitovou přesností, podepsanou 64bitovou přesností nebo bez znaménka 64bitovou přesností, jak je popsáno v §12.4.7.
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
char
typ. Konkrétně platí, že i když majíbyte
typyushort
rozsahy hodnot, které jsou plně reprezentovatelné pomocíchar
typu, implicitní převody z sbajtu, bajtu neboushort
char
neexistují. - Konstanty typu musí být zapsány
char
jako character_literals nebo jako integer_literalv kombinaci s přetypování na znak typu.
Příklad:
(char)10
je stejný jako'\x000A'
. end example
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.10.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.0
získá kladné nekonečno a–1.0 / 0.0
získá záporné nekonečno. end example - 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
float
< 2²⁴ a −149 ≤ e ≤ 104 a pro <, 0< 2⁵³ a −1075 ≤ e ≤ 970.< 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.7).
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ž
double
typ, 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 mimodouble
rozsah, ale následné dělení vrátí dočasný výsledek zpět dodouble
oblasti, 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. end example
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 ≤ <Cmax 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 decimal
s absolutní hodnotou menší než 1.0m
je hodnota je přesná na alespoň 28. desetinné místo. Pro decimal
s absolutní hodnotou větší nebo rovnou 1.0m
je hodnota přesná na nejméně 28 číslic. Na rozdíl od datovýchtypůchm float
double
0.1
V reprezentacích float
double
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í double
.
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 decimal
bez 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 false
vyjá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
false
a 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 nanull
objekt . 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 int
uint
long
. 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 (§19.2).
8.3.11 Typy řazené kolekce členů
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 I1...In
jsou volitelné názvy prvků řazené kolekce členů.
Tato syntaxe je zkratka pro typ vytvořený s typy T1...Tn
z System.ValueTuple<...>
, které musí být sadou obecných typů struktur schopných přímo vyjádřit typy řazené kolekce členů jakékolirity mezi dvěma a sedmi inkluzivními typy.
Nemusí existovat System.ValueTuple<...>
deklarace, která přímo odpovídáritu jakéhokoli typu řazené kolekce členů s odpovídajícím počtem parametrů typu. Místo toho jsou řazené kolekce členů s arity větší než sedm reprezentovány obecným typem System.ValueTuple<T1, ..., T7, TRest>
struktury, který kromě prvků řazené kolekce členů obsahuje Rest
pole obsahující vnořenou hodnotu zbývajících prvků pomocí jiného System.ValueTuple<...>
typu. Takové vnoření může být pozorovatelné různými způsoby, například prostřednictvím přítomnosti Rest
pole. 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 požadováno více než sedm dalších polí, System.ValueTuple<T1, ..., T7, TRest>
se používá rekurzivně.
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 neicializovat0
desetinné číslice, která by mohla představovat pozici prvku řazené kolekce členů, je povolena pouze na pozici označené .X
Volitelné názvy elementů nejsou reprezentovány v ValueTuple<...>
typech a nejsou uloženy v reprezentaci modulu runtime hodnoty řazené kolekce členů. Převody identit (§10.2.2) existují mezi řazenými kolekcemi členů s konvertibilními sekvencemi prvků.
Operátor new
§12.8.17.2 nelze použít se syntaxí new (T1, ..., Tn)
typu řazené kolekce členů . Hodnoty řazené kolekce členů lze vytvořit z výrazů řazené kolekce členů (§12.8.6) nebo použitím operátoru new
přímo na typ vytvořený z ValueTuple<...>
.
Prvky řazené kolekce členů jsou veřejná pole s názvy Item1
, Item2
atd., a lze je přistupovat prostřednictvím přístupu člena k 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.
Poznámka: I když jsou velké řazené kolekce členů reprezentovány vnořenými
System.ValueTuple<...>
hodnotami, je možné ke každému prvku řazené kolekce členů přistupovat přímo sItem...
názvem odpovídajícím jeho umístění. koncová poznámka
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"); // Error: "Item" names do not match their position (int Item2, string Item123) pair5 = (5, "Five"); (int, string) pair6 = new ValueTuple<int, string>(6, "Six"); ValueTuple<int, string> pair7 = (7, "Seven"); Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}");
Typy řazené kolekce členů pro
pair1
,pair2
apair3
jsou 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ázvyItem1
aItem2
odpovídají jejich pozicím, zatímco typ řazené kolekce členů jepair5
zakázán, protože názvyItem2
aItem123
ne.Deklarace pro
pair6
apair7
ukazují, že typy řazené kolekce členů jsou zaměnitelné s konstruované typy formulářeValueTuple<...>
a ženew
operátor je povolen s druhou syntaxí.Poslední řádek ukazuje, že k prvkům řazené kolekce členů může přistupovat
Item
název odpovídající jejich pozici, a také odpovídající název elementu řazené kolekce členů, pokud jsou přítomné v typu. end example
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
HasValue
typubool
- Vlastnost
Value
typuT
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 x
vlastnost . 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.7T
).T?
Typ T?
hodnoty null implementuje žádná rozhraní (§18). 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 object
za .
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 } }
end example
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ává 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 }
end example
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_arguments '>'
;
type_arguments
: 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
Všechny typy lze klasifikovat jako otevřené typy nebo uzavřené typy. 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ů jeho typu nebo argumenty typu obsahující typy jsou otevřeným typem.
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
class
rozhraní nebo parametrem typu, představteC
toto 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 typA
C
jedním z následujících způsobů: - Je-li toto omezení omezením referenčního typu (
class
), musí typA
splňovat jednu z těchto možností:-
A
je typ rozhraní, typ třídy, typ delegáta, typ pole nebo dynamický typ.
Poznámka:
System.ValueType
ASystem.Enum
jsou odkazové typy, které splňují toto omezení. koncová poznámka-
A
je parametr typu, který je znám jako referenční typ (§8.2).
-
- Je-li omezení omezením typu hodnoty (
struct
), musí typA
splňovat jednu z následujících možností:-
A
struct
je typ neboenum
typ, ale ne typ hodnoty null.
Poznámka:
System.ValueType
ASystem.Enum
jsou odkazové typy, které nevyhovují tomuto omezení. koncová poznámka-
A
je parametr typu s omezením typu hodnoty (§15.2.5).
-
- Je-li omezení konstruktoru omezení
new()
, typA
nesmí býtabstract
a musí mít veřejný konstruktor bez parametrů. To je splněné, pokud platí jedna z následujících možností:-
A
je typ hodnoty, protože všechny typy hodnot mají veřejný výchozí konstruktor (§8.3.3). -
A
je parametr typu s omezením konstruktoru (§15.2.5). -
A
je parametr typu s omezením typu hodnoty (§15.2.5). -
A
class
je objekt, který není abstraktní a obsahuje explicitně deklarovaný veřejný konstruktor bez parametrů. -
A
neníabstract
a 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
D
tak,T
abyT
splňovalo omezení stanovené základemclass
B<T>
. Naproti tomu není nutné specifikovat omezení,class
E
protožeList<T>
implementujeIEnumerable
pro libovolnouT
.class B<T> where T: IEnumerable {...} class D<T> : B<T> where T: IEnumerable {...} class E<T> : B<List<T>> {...}
end example
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í (§18.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
null
nelze 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.12.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.
- 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 nelze použít jako unmanaged_type (§8.8).
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ů
Stromy výrazů umožňují, aby výrazy lambda byly reprezentovány jako datové struktury místo spustitelného kódu. Stromy výrazů jsou hodnoty typů stromu výrazů 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 D
delegá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; // Data
Po těchto přiřazeních delegát
del
odkazuje na metodu, která vracíx + 1
, a strom výrazu exp odkazuje na datovou strukturu, která popisuje výrazx => x + 1
.end example
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ě vytvořené stromy 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
dynamic
lze dynamicky svázat (§12.3.3). - Odvození typu (§12.6.3) bude preferovat
dynamic
,object
pokud jsou oba kandidáti. -
dynamic
nelze 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
object
dynamic
- mezi konstruované typy, které jsou stejné při nahrazení
dynamic
object
- mezi typy řazené kolekce členů, které jsou stejné při nahrazení
dynamic
object
- mezi a
- Implicitní a explicitní převody na a z
object
také platí pro a zdynamic
. - Podpisy, které jsou stejné při nahrazování
dynamic
object
, se považují za stejný podpis. -
dynamic
Typ je nerozlišitelný od typuobject
za běhu. - Výraz typu
dynamic
se 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
,int
,uint
long
ulong
char
float
double
decimal
, nebo .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 typ ukazatele (§23.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
null
. Výchozí stav null je možná null. -
non-nullable: Odkaz s možnou
null
hodnotou null by neměl být přiřazen. Výchozí stav null není null.
Poznámka: Typy
R
aR?
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 ,
null
nesmí 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 ignored
end example
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
T
označuje poznámka?
typuT?
T?
s možnou hodnotou null, zatímco neoznačenéT
není 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 warnings
end example
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 suppressed
end example
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
T
odkazu vytvoří?
poznámkaT?
typuT?
s možnou hodnotou null, zatímco neoznačenéT
je 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 hodnota null parametru typu neodpovídá hodnotě null odpovídajícího argumentu typu.
8.9.5 Nullabilities a null states
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í.
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 null 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 nenulového typu odkazu není null.
Poznámka: Možná výchozí stav se používá s nekonstruovanými parametry typu, pokud je typ nenulový, například
string
a 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 } } }
end example
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ý stavp
nenulový. Pokud by byl null, tento příkaz by vyvolán .NullReferenceException
Toto 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ě. end example
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
o
se 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í
P
na hodnotu, která není null, a kompilátor může vydat varování. Při přístupu k vlastnostiP
neexistuje žádné upozornění, protože typ vlastnosti je nenulový odkazový typ. end example
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
DisappearingProperty
nastaveno 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. end example
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. } }
end example
Konec podmíněného normativního textu
ECMA C# draft specification