Sdílet prostřednictvím


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 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 §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 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 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, longa ulong, výchozí hodnota je 0.
    • Pro char, výchozí hodnota je '\x0000'.
    • Pro float, výchozí hodnota je 0.0f.
    • Pro double, výchozí hodnota je 0.0d.
    • Pro decimal, výchozí hodnota je (to znamená 0m hodnota nula s měřítkem 0).
    • Pro bool, výchozí hodnota je false.
    • Pro enum_typeEje 0výchozí hodnota převedena na typ E.
  • 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 typu System.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é ij a k 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é v System.Int32 a členy zděděné z System.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 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 typu int a 'a' je literál typu char. 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, ushortint, 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 do 127včetně.
  • Typ byte představuje 8bitové celé číslo bez znaménka s hodnotami od 0 do 255včetně.
  • Typ short představuje 16bitové celé číslo se signedms a hodnotami od -32768 do 32767včetně.
  • Typ ushort představuje 16bitové celé číslo bez znaménka s hodnotami od 0 do 65535včetně.
  • Typ int představuje 32bitové celé číslo s hodnotami od -2147483648 do 2147483647včetně.
  • Typ uint představuje 32bitové celé číslo bez znaménka s hodnotami od 0 do 4294967295včetně.
  • Typ long představuje 64bitové celé číslo se 64bitovými hodnotami od -9223372036854775808 do 9223372036854775807včetně.
  • Typ ulong představuje 64bitové celé číslo bez znaménka s hodnotami od 0 do 18446744073709551615včetně.
  • Typ char představuje 16bitové celé číslo bez znaménka s hodnotami od 0 do 65535včetně. Sada možných hodnot pro char typ odpovídá znakové sadě Unicode.

    Poznámka: Ačkoli char má stejnou reprezentaci jako ushort, 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 typy ushort rozsahy hodnot, které jsou plně reprezentovatelné pomocí char typu, implicitní převody z sbajtu, bajtu nebo ushortchar 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 , 0float< 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áře x * y / z, kde násobení vytvoří výsledek, který je mimo double rozsah, ale následné dělení vrátí dočasný výsledek zpět do double 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 Emine ≤ 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í 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 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 hodnotu true. 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 na nullobjekt . 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 (§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, Item2atd., 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 s Item... 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, pair2a pair3 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ázvy Item1 a Item2 odpovídají jejich pozicím, zatímco typ řazené kolekce členů je pair5 zakázán, protože názvy Item2 a Item123 ne.

Deklarace pro pair6 a pair7 ukazují, že typy řazené kolekce členů jsou zaměnitelné s konstruované typy formuláře ValueTuple<...>a že new 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 typu bool
  • Vlastnost Value typu T

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.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 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
    }
}

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ředstavte C 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 typ AC jedním z následujících způsobů:
    • Převod totožnosti (§10.2.2)
    • Implicitní převod odkazu (§10.2.8)
    • Převod boxingu (§10.2.9) za předpokladu, že tento typ A je nenulový typ hodnoty.
    • Implicitní odkaz, boxing nebo typ parametr převodu z parametru A typu na C.
  • Je-li toto omezení omezením referenčního typu (class), musí typ A 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 A System.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í typ A splňovat jednu z následujících možností:
    • A struct je typ nebo enum typ, ale ne typ hodnoty null.

    Poznámka: System.ValueType A System.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(), typ A nesmí být abstract 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 aby T splňovalo omezení stanovené základem classB<T>. Naproti tomu není nutné specifikovat omezení, classE protože List<T> implementuje IEnumerable pro libovolnou T.

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 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; // 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ýraz x => 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 objectdynamic
    • mezi konstruované typy, které jsou stejné při nahrazení dynamicobject
    • mezi typy řazené kolekce členů, které jsou stejné při nahrazení dynamicobject
  • Implicitní a explicitní převody na a z object také platí pro a z dynamic.
  • Podpisy, které jsou stejné při nahrazování dynamicobject , se považují za stejný podpis.
  • dynamic Typ je nerozlišitelný od typu object 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, uintlongulongcharfloatdoubledecimal, 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 a R? 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 hodnotou null, 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 ? vygeneruje T? zprávu a typ T? je stejný jako T.
  • Pro jakékoli omezení where T : C?parametru typu, anotace ? vygeneruje C? zprávu a typ C? je stejný jako C.
  • Pro jakékoli omezení where T : U?parametru typu, anotace ? vygeneruje U? zprávu a typ U? je stejný jako U.
  • 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 Toznačuje poznámka ? typu T?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 Todkazu vytvoří ? poznámka T? typu T? 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ýraz default(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ý stav p 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ílem if (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 vlastnosti P 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