Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
8.1 Általános
A C# nyelv típusai két fő kategóriába sorolhatók: referenciatípusok és értéktípusok. Az értéktípusok és a referenciatípusok lehetnek általános típusok, amelyek egy vagy több típusparamétert vesznek igénybe. A típusparaméterek az értéktípusokat és a referenciatípusokat is kijelölhetik.
type
: reference_type
| value_type
| type_parameter
| pointer_type // unsafe code support
;
pointer_type (23.3. §) csak nem biztonságos kódban érhető el (23. §).
Az értéktípusok abban különböznek a referenciatípusoktól, hogy az értéktípusok változói közvetlenül tartalmazzák az adataikat, míg a referenciatípusok változói az adataikra mutató hivatkozásokat tárolnak, amelyek az utóbbiak objektumként ismertek. Hivatkozástípusok esetén két változó hivatkozhat ugyanarra az objektumra, így az egyik változón végzett műveletek hatással lehetnek a másik változó által hivatkozott objektumra. Az értéktípusok esetében a változók mindegyike saját másolatot készít az adatokról, és az egyiken végzett műveletek nem befolyásolhatják a másikat.
Megjegyzés: Ha egy változó referencia- vagy kimeneti paraméter, nem rendelkezik saját tárhellyel, hanem egy másik változó tárolójára hivatkozik. Ebben az esetben a ref vagy out változó valójában egy másik változó aliasa, nem pedig különálló változó. végjegyzet
A C#típusrendszere úgy van egységesítve, hogy bármilyen típusú érték objektumként kezelhető legyen. A C# minden típusa közvetlenül vagy közvetve az object
osztálytípusból származik, és object
minden típus végső alaposztálya. A referenciatípusok értékeit a rendszer egyszerűen objektumként kezeli, ha az értékeket típusként object
tekinti meg. Az értéktípusok értékeit a rendszer a dobozolási és kicsomagolási műveletek végrehajtásával objektumként kezeli (§8.3.13).
Az egyszerűség kedvéért ebben a specifikációban néhány könyvtártípus neve a teljes névvel való megjelölés nélkül van írva. További információért tekintse meg a §C.5-öt .
8.2 Referenciatípusok
8.2.1 Általános
A referenciatípus egy osztálytípus, egy interfésztípus, egy tömbtípus, egy delegált típus vagy dynamic
típus. Minden nem null értékű hivatkozástípushoz egy megfelelő null értékű hivatkozástípus van feljegyzve a ?
típusnévhez fűzve.
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 csak nem biztonságos kódban érhető el (23.3. §). nullable_reference_type-t részletesebben tárgyaljuk a 8.9-ben.
A referenciatípus-érték a típus egy példányára , az utóbbira, az objektumra való hivatkozás. A speciális érték null
kompatibilis az összes referenciatípussal, és azt jelzi, hogy nincs példány.
8.2.2 Osztálytípusok
Az osztálytípus adatstruktúrát határoz meg, amely adattagokat (állandókat és mezőket), függvénytagokat (metódusokat, tulajdonságokat, eseményeket, indexelőket, operátorokat, példánykonstruktorokat, véglegesítőket és statikus konstruktorokat) és beágyazott típusokat tartalmaz. Az osztálytípusok támogatják az öröklést, amely egy olyan mechanizmus, amellyel a származtatott osztályok kiterjeszthetik és specializálhatják az alaposztályokat. Az osztálytípusok példányai object_creation_expressionhasználatával jönnek létre (12.8.17.2. §).
Az osztálytípusokat a §15 leírja.
Bizonyos előre definiált osztálytípusok speciális jelentéssel rendelkeznek a C# nyelvben, az alábbi táblázatban leírtak szerint.
Osztálytípus | Leírás |
---|---|
System.Object |
Az összes többi típus végső alaposztálya. Lásd §8.2.3. |
System.String |
A C# nyelv sztringtípusa. Lásd a 8.2.5. |
System.ValueType |
Az összes értéktípus alaposztálya. Lásd a §8.3.2. |
System.Enum |
Az összes enum típus alaposztálya. Lásd § 19.5. |
System.Array |
Az összes tömbtípus alaposztálya. Lásd a 17.2.2. |
System.Delegate |
Az összes delegate típus alaposztálya. Lásd a §20.1. |
System.Exception |
Az összes kivételtípus alaposztálya. Lásd a 21.3. |
8.2.3 Az objektum típusa
Az object
osztálytípus az összes többi típus végső alaposztálya. A C# minden típusa közvetlenül vagy közvetve az object
osztálytípusból származik.
A kulcsszó object
egyszerűen az előre definiált osztály System.Object
aliasa.
8.2.4 A dinamikus típus
A dynamic
típus bármilyen object
objektumra hivatkozhat. Ha a műveletek típuskifejezésekre dynamic
vannak alkalmazva, a program a feloldásukat elhalasztja, amíg a program le nem fut. Így ha a műveletet nem lehet jogszerűen alkalmazni a hivatkozott objektumra, a fordítás során nem történik hiba. Ehelyett kivétel jelenik meg, ha a művelet feloldása futásidőben meghiúsul.
A dynamic
típust a 8.7. §, a dinamikus kötést pedig a 12.3.1.
8.2.5 A karaktersorozat típusa
A string
típus egy zárt osztály, amely közvetlenül a object
osztályból örököl. Az osztály példányai string
Unicode karaktersztringeket jelölnek.
string
A típus értékei sztringkonstansként írhatók (6.4.5.6. §).
A kulcsszó string
egyszerűen az előre definiált osztály System.String
aliasa.
8.2.6 Interfésztípusok
Az interfész egy szerződést határoz meg. Az interfészt megvalósító osztálynak vagy szerkezetnek be kell tartania a szerződését. Egy interfész több alapillesztőtől örökölhet, az osztály vagy a szerkezet pedig több interfészt is implementálhat.
A felülettípusokat a §18 fejezet írja le.
8.2.7 Tömbtípusok
A tömbök olyan adatszerkezetek, amelyek nulla vagy több változót tartalmaznak, amelyek számítási indexeken keresztül érhetők el. A tömbben található változók, más néven a tömb elemei mind azonos típusúak, és ezt a típust a tömb elemtípusának nevezzük.
A tömbtípusok leírása a §17.
8.2.8 Meghatalmazotttípusok
A delegált egy olyan adatstruktúra, amely egy vagy több metódusra hivatkozik. A metódusok például a hozzájuk tartozó objektumpéldányokra is hivatkoznak.
Megjegyzés: A C vagy C++ nyelvben a delegált legközelebbi megfelelője a függvénymutató, de míg a függvénymutató csak statikus függvényekre hivatkozhat, a delegált statikus és példánymetódusokra is hivatkozhat. Az utóbbi esetben a delegált nem csak a metódus belépési pontjára mutató hivatkozást, hanem arra az objektumpéldányra mutató hivatkozást is tárol, amelyen meg szeretné hívni a metódust. végjegyzet
A delegálás típusait a 20. §-ban ismertetjük.
8.3 Értéktípusok
8.3.1 Általános
Az értéktípus vagy strukturálási vagy enumerálási típus. A C# előre definiált, egyszerű típusoknak nevezett szerkezettípusokat biztosít. Az egyszerű típusokat kulcsszavak azonosítják.
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
;
A referenciatípus változóitól eltérően egy értéktípusú változó csak akkor tartalmazhat értéket null
, ha az értéktípus null értékű (8.3.12. §). Minden nem null értékű értéktípushoz létezik egy megfelelő null értékű típus, amely ugyanazt az értékkészletet és az értéket null
jelöli.
Az érték típusú változóhoz való hozzárendelés létrehozza a hozzárendelt érték másolatát . Ez eltér egy referenciatípusú változóhoz való hozzárendeléstől, amely a hivatkozást másolja, de a hivatkozás által azonosított objektumot nem.
8.3.2 A System.ValueType típusa
Az összes értéktípus implicit módon örököl az class
System.ValueType
osztálytól, amely viszont az osztálytól object
öröklődik. Nem lehetséges, hogy bármely típus értéktípusból származzon, és így az értéktípusok implicit módon lezárva legyenek (15.2.2.3. §).
Ne feledje, hogy System.ValueType
maga nem value_type. Ehelyett ez egy class_type , amelyből az összes value_typeautomatikusan származik.
8.3.3 Alapértelmezett konstruktorok
Minden értéktípus implicit módon deklarál egy nyilvános paraméter nélküli példánykonstruktort , az alapértelmezett konstruktort. Az alapértelmezett konstruktor egy nulla inicializált példányt ad vissza, amely az értéktípus alapértelmezett értéke :
- Az összes simple_type esetében az alapértelmezett érték az összes nulla bit mintázata által létrehozott érték.
- A
sbyte
,byte
,short
,ushort
int
,uint
,long
, ésulong
, az alapértelmezett érték az0
. - Az
char
alapértelmezett értéke'\x0000'
. - Az
float
alapértelmezett értéke0.0f
. - Az
double
alapértelmezett értéke0.0d
. - Az
decimal
alapértelmezett érték0m
(azaz a nulla érték a 0 skálával). - Az
bool
alapértelmezett értékefalse
. - Egy enum_type
E
esetében az alapértelmezett érték0
a típusraE
lesz konvertálva.
- A
- Egy struct_type esetében az alapértelmezett érték az, amelyet úgy kapunk, hogy az összes értéktípus mezőt az alapértelmezett értékükre állítjuk, és az összes referenciatípus mezőt
null
-ra állítunk. - Egy nullable_value_type esetén az alapértelmezett érték olyan példány, amelynek a
HasValue
tulajdonsága hamis. Az alapértelmezett érték a null értékű típus null értékeként is ismert. Ha megkísérli beolvasni egyValue
ilyen érték tulajdonságát, a típusSystem.InvalidOperationException
kivételét okozza (8.3.12.§).
A többi példánykonstruktorhoz hasonlóan az értéktípus alapértelmezett konstruktorát is meghívja a rendszer az new
operátorral.
Megjegyzés: Hatékonysági okokból ez a követelmény nem célja, hogy az implementáció konstruktorhívást hozzon létre. Értéktípusok esetén az alapértelmezett értékkifejezés (§12.8.21) ugyanazt az eredményt adja, mint az alapértelmezett konstruktor. végjegyzet
Példa: Az alábbi kódban a
i
,j
ésk
változók mind nullára vannak inicializálva.class A { void F() { int i = 0; int j = new int(); int k = default(int); } }
záró példa
Mivel minden értéktípus implicit módon rendelkezik nyilvános paraméter nélküli példánykonstruktorsal, a szerkezettípus nem tartalmazhat paraméter nélküli konstruktor explicit deklarációját. A struct típus azonban engedélyezi a paraméteres példánykonstruktorok deklarálását (§16.4.9).
8.3.4 Szerkezettípusok
A szerkezettípus olyan értéktípus, amely konstruktorokat, mezőket, metódusokat, tulajdonságokat, eseményeket, indexelőket, operátorokat, példánykonstruktorokat, statikus konstruktorokat és beágyazott típusokat deklarálhat. A szerkezettípusok deklarációját a 16. § ismerteti.
8.3.5 Egyszerű típusok
A C# előre definiált struct
típusok, úgynevezett egyszerű típusok készletét biztosítja. Az egyszerű típusokat kulcsszavak azonosítják, de ezek a kulcsszavak egyszerűen aliasok az struct
névtér előre definiált System
típusaihoz, az alábbi táblázatban leírtak szerint.
Kulcsszó | alias típus |
---|---|
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 |
Mivel egy egyszerű típus egy struktúratípust aliasosít, minden egyszerű típusnak vannak tagjai.
Például:
int
a tagjai, melyekSystem.Int32
-ban vannak deklarálva és azok, amelyeket aSystem.Object
örökölt, és a következő utasítások engedélyezettek: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
záró példa
Megjegyzés: Az egyszerű típusok abban különböznek a többi struktúratípustól, hogy bizonyos további műveleteket engedélyeznek:
- A legegyszerűbb típusok lehetővé teszik az értékek konstansok írásával történő létrehozását (6.4.5.§), bár a C# általában nem épít ki konstans típusú szerkezettípusokat. Példa:
123
egy típuskonstansint
, és'a'
egy típuskonstanschar
. záró példa- Ha egy kifejezés operandusai mind egyszerű típusállandók, a fordító fordítási időben kiértékelheti a kifejezést. Az ilyen kifejezés constant_expression néven ismert (12.23. §). Más szerkezettípusok által definiált operátorokat tartalmazó kifejezések nem tekinthetők állandó kifejezésnek
- Deklarációkkal
const
az egyszerű típusok állandóit deklarálhatja (15.4. §). Más struktúratípusoknak nem lehetnek konstansai, de a statikus írásvédett mezők hasonló hatást biztosítanak.- Az egyszerű típusok konverziói részt vehetnek más strukturálási típusok által meghatározott konverziós operátorok értékelésében, de a felhasználó által definiált konverziós operátorok soha nem vehetnek részt egy másik felhasználó által definiált konverziós operátor kiértékelésében (10.5.3. §).
végjegyzet.
8.3.6 Integráltípusok
A C# kilenc integráltípust támogat: sbyte
, byte
, short
, ushort
, int
uint
, long
, ulong
és char
. Az integráltípusok a következő méretekkel és értéktartományokkal rendelkeznek:
- A
sbyte
típus az előjeles 8 bites egész számokat jelöli, és az értékeket-128
és127
között, beleértve azokat is, tartalmazza. - A
byte
típus előjel nélküli 8 bites egész számokat jelöl, melyek értékei0
és255
között mozognak, beleértve azokat. - A
short
típus aláírt 16 bites egész számokat képvisel, az értékek a-32768
-től egészen a32767
-ig terjednek, bezárólag. - A
ushort
típus előjelnélküli 16 bites egész számokat jelöl, amelyek értéke0
-től65535
-ig terjed, beleértve azokat is. - A
int
típus az aláírt 32 bites egész számokat jelöli, melyek értékei-2147483648
és2147483647
között vannak, beleértve mindkettőt. - A
uint
típus előjel nélküli, 32 bites egész számokat jelöl, amelyek értéke0
és4294967295
között van, beleértve a határértékeket is. - A
long
típus előjeles 64 bites egész számokat képvisel, amelyek az értékeket-9223372036854775808
-től9223372036854775807
-ig, beleértve tartalmazzák. - A
ulong
típus előjelnélküli, 64 bites egész számokat jelent, amelyek értékei0
-től18446744073709551615
-ig terjednek. - A
char
típus előjelnélküli 16 bites egész számokat jelöl, amelyek értéke0
-től65535
-ig terjed, beleértve azokat is. A típus lehetséges értékei achar
Unicode-karakterkészletnek felelnek meg.Megjegyzés: Bár
char
ugyanazzal a megjelenítéssel rendelkezik, mintushort
a többi, az egyik típuson engedélyezett műveletek nem engedélyezettek a másikon. végjegyzet
Az összes aláírt integráltípus két egymást kiegészítő formátumban jelenik meg.
A integral_type unáris és bináris operátorok mindig aláírt 32 bites pontossággal, aláíratlan 32 bites pontossággal, aláírt 64 bites pontossággal vagy aláíratlan 64 bites pontossággal működnek, amint az a(z) §12.4.7 részben részletezve van.
A char
típus integrált típusként van besorolva, de két módon különbözik a többi integráltípustól:
- Nincsenek előre definiált implicit átalakítások más típusokból a
char
típusba. Különösen annak ellenére, hogy abyte
ésushort
típusok olyan értéktartományokkal rendelkeznek, amelyek teljes mértékben képviselhetők achar
típussal, implicit konverziók sbyte, byte vagyushort
-bőlchar
-be nem léteznek. - A
char
típus állandóit karakter literálokként vagy egész literálokként kell írni, egy karakter típusú konverzióval kombinálva.
Példa:
(char)10
ugyanaz, mint'\x000A'
a . záró példa
Az checked
integrál típusú aritmetikai műveletek és unchecked
átalakítások túlcsordulási ellenőrzésének szabályozására az operátorok és az utasítások szolgálnak (12.8.20. §).
checked
Egy kontextusban a túlcsordulás fordítási időt eredményez, vagy egy hibát okozSystem.OverflowException
.
unchecked
Egy kontextusban a rendszer figyelmen kívül hagyja a túlcsordulásokat, és elveti a céltípusba nem illő, magasrendű biteket.
8.3.7 Lebegőpontos típusok
A C# két lebegőpontos típust támogat: float
és double
. A float
típusokat a double
32 bites egypontos és a 64 bites dupla pontosságú IEC 60559 formátumok képviselik, amelyek a következő értékkészleteket biztosítják:
- Pozitív nulla és negatív nulla. A legtöbb esetben a pozitív nulla és a negatív nulla ugyanúgy viselkedik, mint az egyszerű nulla, de bizonyos műveletek megkülönböztetik a kettőt (12.10.3. §).
- Pozitív végtelen és negatív végtelen. A végteleneket olyan műveletek állítják elő, mint a nem nulla szám nullával való osztása.
Például:
1.0 / 0.0
pozitív végtelent hoz, és–1.0 / 0.0
negatív végtelent ad. záró példa - A Nem-a-Szám érték, gyakran rövidítve NaN. A naN-okat érvénytelen lebegőpontos műveletek állítják elő, például a nullát nullával osztják el.
- Az nem nulla értékek véges készlete, amelyek s alakúak és m × 2ᵉ, ahol s 1 vagy −1, és m, valamint e értékei a konkrét lebegőpontos típus alapján kerülnek meghatározásra:
float
0 <m< 2²⁴ és −149 ≤ e ≤ 104, valamint az esetén adouble
, 0 <m< 2⁵³ és −1075 ≤ e ≤ 970. A denormalizált lebegőpontos számok érvényes nem nulla értékeknek minősülnek. A C# nem követeli meg és nem is tiltja, hogy egy megfelelő implementáció támogassa a denormalizált lebegőpontos számokat.
A float
típus körülbelül 1,5 × 10⁻⁴⁵ és 3,4 × 10³⁸ közötti értékeket jelölhet 7 számjegy pontossággal.
A double
típus körülbelül 5,0 × 10⁻³²⁴ és 1,7 × 10³⁰⁸ közötti értékeket jelölhet 15-16 számjegy pontossággal.
Ha egy bináris operátor valamelyik operandusa lebegőpontos típus, akkor a standard numerikus előléptetések alkalmazásra kerülnek, amint azt a 12.4.7 részletezi, és a művelet float
vagy double
pontossággal történik.
A lebegőpontos operátorok, beleértve a hozzárendelési operátorokat, soha nem okoznak kivételeket. Ehelyett kivételes esetekben a lebegőpontos műveletek nullát, végtelent vagy NaN-t eredményeznek az alábbiak szerint:
- A lebegőpontos művelet eredménye a célformátum legközelebbi ábrázolható értékére lesz kerekítve.
- Ha egy lebegőpontos művelet eredményének mérete túl kicsi a célformátumhoz, a művelet eredménye pozitív nulla vagy negatív nulla lesz.
- Ha egy lebegőpontos művelet eredményének mérete túl nagy a célformátumhoz, a művelet eredménye pozitív végtelenné vagy negatív végtelenné válik.
- Ha egy lebegőpontos művelet érvénytelen, a művelet eredménye NaN lesz.
- Ha egy lebegőpontos művelet egyik vagy mindkét operandusa NaN, a művelet eredménye NaN lesz.
A lebegőpontos műveletek nagyobb pontossággal végezhetők el, mint a művelet eredménytípusa. Ha egy lebegőpontos típus értékét a típus pontos pontosságára szeretné kényszeríteni, explicit vetítés (12.9.7. §) használható.
Példa: Egyes hardverarchitektúrák támogatják a "kiterjesztett" vagy a "hosszú dupla" lebegőpontos típust, amely nagyobb tartományt és pontosságot biztosít, mint a
double
típus, és implicit módon elvégzi az összes lebegőpontos műveletet ezzel a nagyobb pontosságú típussal. Az ilyen hardverarchitektúrák lebegőpontos műveletek kisebb pontossággal való végrehajtása csak a teljesítmény jelentős költsége árán valósítható meg, és ahelyett, hogy le kellene mondani mind a teljesítményről, mind a pontosságról, a C# lehetővé teszi, hogy minden lebegőpontos művelethez nagyobb pontosságú típust használjunk. A pontosabb eredményeken kívül ez ritkán van mérhető hatással. Az űrlapx * y / z
kifejezéseiben azonban , ahol a szorzás a tartományon kívül esődouble
eredményt hoz létre, de az ezt követő osztás visszahozza az ideiglenes eredményt adouble
tartományba, az a tény, hogy a kifejezés kiértékelése magasabb tartományformátumban történik, véges eredményt eredményezhet a végtelen helyett. záró példa
8.3.8 A decimális típus
A decimal
típus egy 128 bites adattípus, amely pénzügyi és pénzügyi számításokhoz használható. A decimal
típus a legalább -7,9 × 10⁻²⁸ és 7,9 × 10²⁸ közötti értékeket is jelölheti, legalább 28 jegyű pontossággal.
A decimal
típus véges értékei a (–1)v × c × 10⁻e formában vannak, ahol a v jel 0 vagy 1, a < együtthatót az 0 ≤ c ≤ Cmax adja meg, és a skála e úgy van, hogy Emin ≤ e ≤ Emax, ahol Cmax legalább 1 × 10²⁸, Emin ≤ 0, és Emax ≥ 28. A decimal
típus nem feltétlenül támogatja az aláírt nullákat, a végteleneket vagy a NaN-eket.
Az A decimal
egy tízes hatvány által skálázott egész számként jelenik meg. Azoknál decimal
-oknál, amelyek abszolút értéke kisebb, mint 1.0m
, az érték legalább a 28. tizedesjegyig pontos. Azoknál az decimal
értékeknél, amelyek abszolút értéke nagyobb vagy egyenlő 1.0m
, az érték legalább 28 számjegy pontossággal bír. Az float
és double
adattípusokkal ellentétben a tizedes törtek, például az 0.1
, pontosan ábrázolhatók a tizedes reprezentációban. Az float
és double
reprezentációkban az ilyen számok gyakran nem végződő bináris kibővítésekkel rendelkeznek, így ezek a reprezentációk hajlamosabbak a kerekítési hibákra.
Ha egy bináris operátor bármelyik operandusa decimal
típusú, akkor a szabványos numerikus előléptetések, ahogyan az a §12.4.7 részben szerepel, alkalmazásra kerülnek, és a művelet double
precizitással hajtódik végre.
A típusértékeken decimal
végzett művelet eredménye az, amely egy pontos eredmény kiszámításából (az egyes operátorok számára meghatározott skálázás megőrzéséből) és a ábrázolásnak megfelelő kerekítésből ered. Az eredmények a legközelebbi ábrázolható értékre vannak kerekítve, és ha egy eredmény egyenlően közel van két megjeleníthető értékhez, ahhoz az értékhez, amelynek páros száma a legkisebb számjegypozícióban van (ezt "banker kerekítésének" nevezzük). Vagyis az eredmények legalább a 28. tizedesjegyig pontosak. Vegye figyelembe, hogy a kerekítés nullát eredményezhet egy nem nulla értékből.
Ha egy decimal
aritmetikai művelet olyan eredményt hoz létre, amelynek nagysága túl nagy a decimal
formátumhoz, egy System.OverflowException
kivételt dob.
A decimal
típus nagyobb pontosságú, de kisebb tartományt tartalmazhat, mint a lebegőpontos típusok. Így a lebegőpontos típusok decimal
átalakítása túlcsordulási kivételeket eredményezhet, a lebegőpontos típusok közötti decimal
átalakítások pedig a pontosság elvesztését vagy a túlcsordulási kivételek elvesztését okozhatják. Ezen okokból nem léteznek implicit konverziók a lebegőpontos típusok és decimal
között, és explicit leadások nélkül fordítási időhiba lép fel, ha a lebegőpontos és decimal
operandusok közvetlenül egy kifejezésben vannak összekeverve.
8.3.9 Bool típus
A bool
típus logikai mennyiségeket jelöl. A lehetséges típusértékek bool
a következők: true
és false
. A false
reprezentációját a §8.3.3 leírja. Annak ellenére, hogy true
ábrázolása nem meghatározott, különböznie kell false
ábrázolásától.
A többi értéktípus között bool
nem léteznek standard konverziók. Az `bool
` típus különálló és elkülönül az integráltípusoktól, `bool
` értéket nem lehet integrál értékként használni, és fordítva.
Megjegyzés: A C és a C++ nyelvekben nulla integrál- vagy lebegőpontos érték, illetve null mutató konvertálható logikai értékké
false
, és nem nulla integrál- vagy lebegőpontos értékké, illetve nem null mutató konvertálható logikai értékkétrue
. A C#-ban az ilyen átalakítások úgy valósulnak meg, hogy egy egész vagy lebegőpontos értéket explicit módon összehasonlítunk nullával, vagy egy objektumhivatkozást explicit módon összehasonlítunknull
-val. végjegyzet
8.3.10 Enumerálási típusok
Az enumerálási típus egy különálló típus nevesített állandókkal. Minden enumerálási típus rendelkezik egy mögöttes típussal, amely lehet byte
, sbyte
, short
, ushort
, int
, uint
long
vagy ulong
. Az enumerálási típus értékeinek halmaza megegyezik az alapul szolgáló típus értékeinek halmazával. Az enumerálási típus értékei nem korlátozódnak az elnevezett állandók értékeire. Az enumerálási típusok enumerálási deklarációkkal vannak definiálva (19.2. §).
8.3.11 Tupus típusok
A tupla típus egy értékek rendezett, rögzített hosszúságú sorozatát jelöli, opcionális nevekkel és egyéni típusokkal. A tuple típusú elemek számát aritásának nevezzük. A tuple típus (T1 I1, ..., Tn In)
úgy van megírva, hogy n ≥ 2, ahol az azonosítók I1...In
választható tuple elemnevek.
Ez a szintaxis annak a típusnak a rövidítése, amely a T1...Tn
típusokból egy System.ValueTuple<...>
típusú struktúrát alkot, és amely egy olyan általános szerkezettípusok halmozódását képviseli, amelyek képesek közvetlenül ábrázolni a két és hét közötti elemszámú n-es értékeket.
Nincs szükség olyan System.ValueTuple<...>
deklarációra, amely közvetlenül megfelel bármely tömb típus tetszőleges aritásának vagy a megfelelő számú típusparaméterek számának. Ehelyett a hétnél nagyobb aritású tuplek egy általános szerkezet típus System.ValueTuple<T1, ..., T7, TRest>
használatával vannak jelölve, amely a tuple elemek mellett egy Rest
mezővel is rendelkezik, amely a többi elem beágyazott értékét tartalmazza egy másik System.ValueTuple<...>
típus használatával. Az ilyen beágyazás különböző módokon figyelhető meg, például egy Rest
mező megléte révén. Ha csak egyetlen további mezőre van szükség, a rendszer az általános szerkezettípust System.ValueTuple<T1>
használja; ez a típus önmagában nem számít rekordtípusnak. Ha több mint hét további mezőre van szükség, System.ValueTuple<T1, ..., T7, TRest>
rekurzívan kell használni.
A típuson belüli elemneveknek különbözőnek kell lenniük. A többszörös elem név formája, mint ItemX
, ahol X
bármilyen nem-0
-kezdetű tizedes számjegyek sorozata, amely a többszörös elem pozícióját képviselheti, csak az általuk jelölt pozícióban engedélyezett.
A választható elemnevek nem jelennek meg a ValueTuple<...>
típusok között, és nem tárolódnak egy rekordérték futásidejű ábrázolásában. Az identitáskonvertálások (§10.2.2) az elemtípusok identitáskonvertálható sorozataival rendelkező tupelok között léteznek.
A new
§12.8.17.2 operátor nem alkalmazható a tupletípus szintaxisával new (T1, ..., Tn)
. A tuple értékek létrehozhatóak tuple-kifejezésekből (12.8.6. §), vagy az new
operátor ValueTuple<...>
alapján konstruált típusra közvetlenül alkalmazva.
A tuple elemek olyan nyilvános mezők, ezeknek a neve Item1
, Item2
, stb., és egy tagi hozzáféréssel érhetők el egy tuple értéken (12.8.7. §). Továbbá, ha az n-tuple típusnak van egy neve egy adott elemhez, akkor ez a név használható a szóban forgó elem eléréséhez.
Megjegyzés: Még akkor is, ha a nagy tupelokat beágyazott
System.ValueTuple<...>
értékekkel reprezentálják, minden egyes tupelelem közvetlenül elérhető a pozíciójának megfelelőItem...
névvel. végjegyzet
Példa: Az alábbi példákat tekintve:
(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}");
A
pair1
,pair2
éspair3
tuple típusok mind érvényesek, és az elemeik lehetnek névtelenek, néhánynak vagy mindegyiknek lehet neve.A rekordtípus
pair4
azért érvényes, mert a nevekItem1
ésItem2
a pozíciójuknak megfelelnek, míg a rekordtípuspair5
nem engedélyezett, mert a nevekItem2
ésItem123
nem egyeznek meg.A deklarációk
pair6
éspair7
azt mutatják be, hogy az n-tuple típusok felcserélhetők aValueTuple<...>
alakú konstrukciókkal létrehozott típusokkal, és hogy aznew
operátor használata az utóbbi szintaxissal lehetséges.Az utolsó sorban látható, hogy a
Item
rekordelemek a helyüknek megfelelő névvel, valamint a megfelelő rekordelem nevével érhetők el, ha a típusban szerepel. záró példa
8.3.12 Null értékű értéktípusok
A null értékű értékek a mögöttes típus összes értékét, valamint egy további null értéket jelölhetnek. A null értékű típust így írjuk: T?
, ahol T
az alapul szolgáló típus. Ez a szintaxis a System.Nullable<T>
rövidített formája, és a két forma felcserélhetően használható.
Ezzel szemben a nem nullázható értéktípus minden olyan értéktípus, amely nem System.Nullable<T>
, annak rövidített formája T?
(bármely T
), valamint minden olyan típusparaméter, amely korlátozva van nem nullázható értéktípussá (vagyis bármely értéktípus-korlátozással rendelkező típusparaméter (§15.2.5)). A System.Nullable<T>
típus megadja az értéktípusra vonatkozó T
korlátozást, ami azt jelenti, hogy a null értékű értékek alapjául szolgáló típus bármilyen nem null értékű lehet. A null értékre képes értéktípus mögöttes típusa nem lehet null értékre képes értéktípus vagy referenciatípus. Például int??
érvénytelen típus. A null értékű hivatkozástípusokat a 8.9-ben tárgyalják.
Egy null értékű példány T?
két nyilvános, csak olvasható tulajdonsággal rendelkezik.
- Egy
HasValue
tulajdonság, amelybool
típusú - Egy
Value
tulajdonság, amelyT
típusú
Egy olyan példány, amelyről HasValue
azt true
mondják, hogy nem null értékű. A nem null értékű példány ismert értéket tartalmaz, és Value
ezt az értéket adja vissza.
Az a példány, amelynél a HasValue
false
nullának tekintendő. A nullpéldányok nem definiált értékkel rendelkeznek. Amikor megkíséreljük elolvasni a Value
egy null példányból, egy System.InvalidOperationException
keletkezik. A null értékű példány Érték tulajdonságának elérésének folyamatát kicsomagolásnak nevezzük.
Az alapértelmezett konstruktor mellett minden null értékű értéktípushoz T?
tartozik egy nyilvános konstruktor egyetlen típusú T
paraméterrel. Egy x
típusú T
érték esetén a konstruktor hívása a következő formában történik:
new T?(x)
Létrehoz egy nem null értékű példányt T?
, amelynek a Value
tulajdonsága x
. Egy adott érték null értékű típusának nem null értékű példányának létrehozásának folyamatát burkolásnak nevezzük.
Implicit konverziók érhetők el a null
literáltól T?
-re (§10.2.7) és T
-től T?
-re (§10.2.6).
A null értékű értéktípus T?
nem valósít meg interfészeket (18. §). Ez különösen azt jelenti, hogy nem implementál olyan felületet, amelyet az alapul szolgáló típus T
csinál.
8.3.13 Csomagolás és kicsomagolás
A dobozolás és a kicsomagolás fogalma hidat biztosít value_type és reference_type között azáltal, hogy lehetővé teszi egy value_type bármely értékének a object
típusra és onnan való konvertálását. A dobozolás és a kicsomagolás lehetővé teszi a típusrendszer egységes nézetét, ahol végső soron bármilyen típusú értéket object
-ként lehet kezelni.
A dobozolást részletesebben a §10.2.9-ben , a kicsomagolást pedig a §10.3.7-ben írtuk le.
8.4 Épített típusok
8.4.1 Általános
Az általános típusdeklaráció önmagában egy kötetlen általános típust jelöl, amelyet "tervként" használnak számos különböző típus létrehozásához típusargumentumok alkalmazásával. A típusargumentumok szögletes zárójelekbe vannak írva (<
és >
) közvetlenül az általános típus neve után. A legalább egy típusargumentumot tartalmazó típust konstruktált típusnak nevezzük. A konstruktált típus a nyelv legtöbb helyén használható, ahol a típusnév megjeleníthető. Kötetlen általános típus csak egy typeof_expression belül használható (12.8.18. §).
A létrehozott típusok egyszerű nevekként is használhatók a kifejezésekben (12.8.4. §), vagy egy tag elérésekor (12.8.7. §).
Egy namespace_or_type_name kiértékelésekor csak a megfelelő számú típusparaméterrel rendelkező általános típusokat veszi figyelembe a rendszer. Így ugyanazt az azonosítót használhatja a különböző típusok azonosítására, feltéve, hogy a típusok különböző számú típusparamétert használnak. Ez akkor hasznos, ha egyazon program általános és nem általános osztályait keveri.
Példa:
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 } }
záró példa
A namespace_or_type_name produkciók névkeresésének részletes szabályai a 7.8 pontban vannak leírva. A kétértelműségek feloldását ezekben az alkotásokban a 6.2.5 írja le. Egy type_name akkor is azonosíthat egy létrehozott típust, ha közvetlenül nem ad meg típusparamétereket. Ez akkor fordulhat elő, ha egy típus egy általános class
deklarációban van beágyazva, és a rendszer implicit módon használja a névkereséshez az azt tartalmazó deklaráció példánytípusát (15.3.9.7. §).
Példa:
class Outer<T> { public class Inner {...} public Inner i; // Type of i is Outer<T>.Inner }
záró példa
Egy nem enumként létrehozott típus nem használható unmanaged_type (§8.8).
8.4.2 Típusargumentumok
A típusargumentumok listájának minden argumentuma egyszerűen egy típus.
type_argument_list
: '<' type_argument (',' type_argument)* '>'
;
type_argument
: type
| type_parameter nullable_type_annotation?
;
Minden típusargumentumnak meg kell felelnie a megfelelő típusparaméterre vonatkozó korlátozásoknak (15.2.5. §). Egy hivatkozástípus argumentuma, amelynek nullképessége nem egyezik meg a típusparaméter nullhatóságával, megfelel a kényszernek; figyelmeztetést adhat ki.
8.4.3 Nyitott és zárt típusok
Minden típus besorolható nyitott vagy zárt típusként. A nyitott típus olyan típus, amely típusparamétereket tartalmaz. Pontosabban:
- A típusparaméter egy nyitott típust határoz meg.
- A tömbtípus nyitott típus, ha és csak akkor, ha az elemtípusa nyitott típus.
- A létrehozott típus nyitott típus, ha és csak akkor, ha egy vagy több típusargumentum nyitott típus. A létrehozott beágyazott típus nyitott típus, ha és csak akkor, ha egy vagy több típusargumentuma vagy egy vagy több tartalmazó típusargumentuma nyitott típus.
A zárt típus olyan típus, amely nem nyitott típus.
Futtatáskor az általános típusú deklaráción belüli összes kód végrehajtása egy zárt, konstruktált típus kontextusában történik, amelyet típusargumentumok alkalmazásával hoztak létre az általános deklarációra. Az általános típus minden típusparamétere egy adott futásidejű típushoz van kötve. Az összes utasítás és kifejezés futásidejű feldolgozása mindig zárt típusok esetén történik, a nyitott típusok pedig csak a fordítási idő feldolgozása során.
Két zárt épített típus identitáskonvertálható (§10.2.2), ha ugyanabból a kötetlen általános típusból vannak létrehozva, és mindegyik típusargumentum között létezik egy identitásátalakítás. A megfelelő típusargumentumok lehetnek zárt, konstruktált típusok vagy tupulok, amelyek azonosítható módon konvertálhatók. Az identitáskonvertálható zárt típusok egyetlen statikus változókészlettel osztoznak. Ellenkező esetben minden zárt építésű típus saját statikus változókészlettel rendelkezik. Mivel egy nyitott típus futásidőben nem létezik, nincsenek statikus változók társítva egy nyitott típushoz.
8.4.4 Kötött és kötetlen típusok
A kötetlen típus kifejezés nem általános típusra vagy kötetlen általános típusra hivatkozik. A kötött kifejezés nem általános típusra vagy konstruktált típusra utal.
A kötetlen típus a típusdeklarációval deklarált entitásra vonatkozik. A kötetlen általános típus önmagában nem típus, és nem használható változó, argumentum vagy visszatérési érték típusaként, illetve alaptípusként. A kötetlen általános típusra csak a typeof
kifejezés hivatkozhat (12.8.18. §).
8.4.5 A korlátozások kielégítése
Amikor egy létrehozott típusra vagy általános metódusra hivatkozik, a megadott típusargumentumokat a rendszer ellenőrzi az általános típuson vagy metóduson deklarált típusparaméter-megkötésekkel (15.2.5. §). Az egyes where
záradékok esetében a névvel ellátott típusparaméternek megfelelő típusargumentumot A
a rendszer az alábbiak szerint ellenőrzi az egyes kényszerek között:
- Ha a kényszer típus
class
, illesztőtípus vagy típusparaméter, akkor aC
kényszert a megadott típusargumentumokkal helyettesítsük a kényszerben megjelenő típusparaméterek helyett. A kényszer kielégítése érdekében a típusA
az alábbiak egyikével konvertálható típussáC
: - Ha a kényszer a referenciatípus kényszere (
class
), a típusnakA
meg kell felelnie az alábbiak egyikének:-
A
interfésztípus, osztálytípus, delegálás típusa, tömbtípus vagy dinamikus típus.
Megjegyzés:
System.ValueType
ésSystem.Enum
olyan referenciatípusok, amelyek megfelelnek ennek a kényszernek. végjegyzet-
A
olyan típusparaméter, amely ismerten referenciatípus (8.2. §).
-
- Ha a kényszer az értéktípus kényszere (
struct
), a típusnakA
meg kell felelnie az alábbiak egyikének:-
A
egystruct
típus vagyenum
típus, de nem null értékmegengedő típus.
Megjegyzés:
System.ValueType
ésSystem.Enum
olyan referenciatípusok, amelyek nem felelnek meg ennek a kényszernek. végjegyzet-
A
olyan típusparaméter, amely értéktípus-korlátozással rendelkezik (15.2.5. §).
-
- Ha a kényszer a konstruktor kényszere
new()
, akkor a típusA
nem lehetabstract
, és rendelkeznie kell egy nyilvános, paraméter nélküli konstruktorral. Ez akkor teljesül, ha az alábbiak egyike igaz:-
A
egy értéktípus, mivel minden értéktípus rendelkezik nyilvános alapértelmezett konstruktorsal (8.3.3. §). -
A
olyan típusparaméter, amely konstruktor-kényszerrel rendelkezik (15.2.5. §). -
A
olyan típusparaméter, amely értéktípus-korlátozással rendelkezik (15.2.5. §). -
A
class
nem absztrakt, és egy explicit módon deklarált, paraméterek nélküli nyilvános konstruktort tartalmaz. -
A
nemabstract
, és van alapértelmezett konstruktora (15.11.5. §).
-
Fordítási időhiba akkor fordul elő, ha egy típusparaméter egy vagy több kényszerét nem teljesítik a megadott típusargumentumok.
Mivel a típusparaméterek nem öröklődnek, a kényszerek sem öröklődnek.
Példa: Az alábbiakban
D
meg kell adnia a típusparaméterreT
vonatkozó kényszert, hogyT
megfeleljen az alapclass
B<T>
által előírt kényszernek. Ezzel szembenclass
E
nem kell kényszert megadnia, mertList<T>
implementáljaIEnumerable
bármelyikT
.class B<T> where T: IEnumerable {...} class D<T> : B<T> where T: IEnumerable {...} class E<T> : B<List<T>> {...}
záró példa
8.5 Típusparaméterek
A típusparaméter olyan azonosító, amely egy olyan értéktípust vagy referenciatípust jelöl ki, amelyhez a paraméter futásidőben van kötve.
type_parameter
: identifier
;
Mivel a típusparaméterek számos különböző típusargumentummal hozhatók létre, a típusparaméterek működése és korlátozásai némileg eltérnek a többi típustól.
Megjegyzés: Ezek a következők:
- A típusparaméter nem használható közvetlenül alaposztály (15.2.4.2. §) vagy interfész (18.2.4. §) deklarálásához.
- A típusparaméterekre vonatkozó tagkeresési szabályok a típusparaméterre alkalmazott korlátozásoktól függnek, ha vannak ilyenek. Azok részletesen a §12.5 pontban vannak leírva.
- A típusparaméter elérhető konverziói a típusparaméterre alkalmazott korlátozásoktól függnek, ha vannak ilyenek. Ezek részletes leírása a 10.2.12 . és a 10.3.8.
- A literál
null
nem konvertálható típusparaméter által megadott típussá, kivéve, ha a típusparaméter ismert hivatkozástípus (10.2.12. §). Ehelyett azonban egy alapértelmezett kifejezés (12.8.21. §) használható. Ezenkívül egy típusparaméter által megadott típussal rendelkező érték összehasonlítható a null értékkel==
és!=
a (12.12.7. §)-val, kivéve, ha a típusparaméter értéktípus-korlátozással rendelkezik.- Kifejezés
new
(§12.8.17.2) csak akkor használható típusparaméterrel, ha a típusparamétert constructor_constraint vagy értéktípus-korlátozás korlátozza (15.2.5. §).- Egy típusparaméter nem használható egy attribútumon belül sehol.
- A típusparaméter nem használható taghozzáférésben (12.8.7. §) vagy típusnévben (7.8. §) statikus tag vagy beágyazott típus azonosítására.
- Típusparaméter nem használható unmanaged_type (8.8. §).
végjegyzet
Típusként a típusparaméterek pusztán fordítási idejű konstrukciók. Futásidőben minden típusparaméter egy futásidejű típushoz van kötve, amelyet egy típusargumentum megadásával adott meg az általános típusdeklarációhoz. Így egy típusparaméterrel deklarált változó típusa futásidőben egy zárt összetett típus lesz §8.4.3. A típusparamétereket tartalmazó összes utasítás és kifejezés futásidejű végrehajtása az adott paraméter típusargumentumaként megadott típust használja.
8.6 Kifejezésfatípusok
A kifejezésfák lehetővé teszik, hogy a lambda-kifejezések végrehajtható kód helyett adatstruktúrákként legyenek ábrázolva. A kifejezésfák az űrlap értékei, ahol System.Linq.Expressions.Expression<TDelegate>
bármilyen delegálási típus szerepel. A specifikáció hátralevő részében ezekre a típusokra a rövidítéssel Expression<TDelegate>
hivatkozunk.
Ha a lambda kifejezésből delegált típusba D
történő átalakítás is létezik, akkor a kifejezésfa típusára Expression<TDelegate>
is van átalakítás. Míg a lambda kifejezés delegált típusúvá alakítása létrehoz egy delegáltat, amely a lambda kifejezés végrehajtható kódjára hivatkozik, a kifejezésfa típusra való átalakítás létrehozza a lambda kifejezés kifejezésfa-reprezentációját. Az átalakítás további részletei a 10.7.3 szakaszban vannak megadva.
Példa: A következő program egy lambda kifejezést jelöl végrehajtható kódként és kifejezésfaként is. Mivel a konvertálás a következőre
Func<int,int>
vonatkozik, a konvertálás a következőreExpression<Func<int,int>>
is vonatkozik:Func<int,int> del = x => x + 1; // Code Expression<Func<int,int>> exp = x => x + 1; // Data
Ezeket a hozzárendeléseket követve a delegált
del
egy visszaadottx + 1
metódusra hivatkozik, a kifejezésfa pedig egy, a kifejezéstx => x + 1
leíró adatstruktúrára hivatkozik.záró példa
Expression<TDelegate>
olyan példánymetódust Compile
biztosít, amely a következő típusú TDelegate
delegáltat állítja elő:
Func<int,int> del2 = exp.Compile();
A meghatalmazott meghívása a kifejezésfa által képviselt kód végrehajtását eredményezi. A fenti del
del2
definíciók tehát egyenértékűek, és a következő két állításnak ugyanaz a hatása:
int i1 = del(1);
int i2 = del2(1);
A kód végrehajtása után, i1
és i2
mindkettő értéke 2
lesz.
A Expression<TDelegate>
által biztosított API-felület további részletei implementációtól függőek a fent leírt Compile
metódus követelményein túl.
Megjegyzés: Bár a kifejezésfákhoz megadott API részletei implementálásra vannak definiálva, a megvalósítás várhatóan a következő lesz:
- Kód engedélyezése egy lambdakifejezésből való átalakítás eredményeként létrehozott kifejezésfa szerkezetének vizsgálatához és megválaszolásához
- A kifejezésfák létrehozásának engedélyezése programozott módon a felhasználói kódban
végjegyzet
8.7 A dinamikus típus
A dynamic
típus dinamikus kötést használ, amint azt részletesen leírják a 12.3.2 szakaszban, szemben a statikus kötésekkel, amelyeket minden más típus használ.
A típus dynamic
a következő szempontok kivételével azonosnak object
tekinthető:
- A típuskifejezéseken
dynamic
végzett műveletek dinamikusan kötöttek lehetnek (12.3.3. §). - A típuskövetkeztetés (12.6.3. §)
dynamic
-t részesíti előnybenobject
-vel szemben, ha mindkettő jelölt. -
dynamic
nem használható- Az objektum-létrehozási kifejezés típusa (12.8.17.2. §)
- class_base (§15.2.4)
- egy predefined_type egy member_access során (§ 12.8.7.1)
- az operátor operandusa
typeof
- attribútumargumentum
- kényszer
- bővítménymetódus típusa
- egy típusargumentum bármely részének a struct_interfaces (§16.2.5) vagy az interface_type_list (§15.2.4.1) részeként történő beillesztése.
A fentiek miatt a következők igazak:
- Van egy implicit identitás átalakítás.
-
object
ésdynamic
között - típusok között, amelyek megegyeznek akkor, ha
dynamic
helyéreobject
van cserélve - azonos gyűjttípusok között, amikor `
dynamic
` helyébe `object
` lép
-
- Implicit és explicit konverziók
object
ésdynamic
között is érvényesek. - Azok az aláírások, amelyek a csere
dynamic
object
során azonosak, azonos aláírásnak minősülnek. - A típus
dynamic
nem megkülönböztethető a típustólobject
futásidőben. - Az ilyen típusú
dynamic
kifejezéseket dinamikus kifejezésnek nevezzük.
8.8 Nem felügyelt típusok
unmanaged_type
: value_type
| pointer_type // unsafe code support
;
A unmanaged_type olyan típus, amely nem reference_type vagy nem type_parameter, és nem korlátozva van a felügyelet nélküli állapotra, és nem tartalmaz olyan példánymezőket, amelyek típusa nem unmanaged_type. Más szóval egy unmanaged_type az alábbiak egyike:
-
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
,float
,double
,decimal
vagybool
. - Bármelyik enum_type.
- Minden felhasználó által definiált struct_type, amely csak unmanaged_typepéldánymezőit tartalmazza.
- Minden olyan típusparaméter, amely nem felügyeltnek van korlátozva.
- Bármely pointer_type (§23.3).
8.9 Referenciatípusok és nullázhatóság
8.9.1 Általános
A null értékű hivatkozástípusokat úgy jelölik, hogy hozzáfűznek egy nullable_type_annotation (?
) egy nem null értékű hivatkozástípushoz. Nincs szemantikai különbség a nem null értékű referenciatípus és a hozzá tartozó null értékű típus között, mindkettő lehet objektumra mutató hivatkozás vagy null
. A nullable_type_annotation jelenléte vagy hiánya azt jelzi, hogy egy kifejezés null értékeket kíván-e engedélyezni. A fordítók diagnosztikát biztosíthatnak, ha egy kifejezést nem a szándéknak megfelelően használnak. Egy kifejezés null állapota a §8.9.5-ben van meghatározva. Az identitásátalakítás egy null értékű hivatkozástípus és annak megfelelő nem null értékű hivatkozástípus (10.2.2. §) között létezik.
A hivatkozástípusok esetében a nullhihetőségnek két formája van:
-
nullable: Nullable-reference-type lehet hozzárendelni
null
. Az alapértelmezett null állapot talán-null. -
nem null értékű: A nem null értékű hivatkozás nem rendelhető
null
értékhez. Alapértelmezett null állapota nem null.
Jegyzet: A típusokat
R
R?
ugyanaz a mögöttes típus jelöli.R
Az ilyen típusú változók tartalmazhatnak egy objektumra mutató hivatkozást, vagy az értéketnull
, amely jelzi a "nincs hivatkozás" állapotot. végjegyzet
A null értékű referenciatípus és a hozzá tartozó nem null értékű referenciatípus közötti szintaktikai különbség lehetővé teszi, hogy a fordító diagnosztikát hozzon létre. A fordítónak engedélyeznie kell a nullable_type_annotation, ahogy az a §8.2.1-ben meg van határozva. A diagnosztikának figyelmeztetésekre kell korlátozódnia. Sem a null értékű széljegyzetek jelenléte, sem hiánya, sem a null értékű környezet állapota nem módosíthatja a program fordítási idejét vagy futásidejét, kivéve a fordításkor létrehozott diagnosztikai üzenetek változásait.
8.9.2 Nem null értékű referenciatípusok
A nem null értékű hivatkozástípus az űrlap T
referenciatípusa, ahol T
a típus neve szerepel. A nem null értékű változók alapértelmezett null állapota nem null. Figyelmeztetések akkor hozhatók létre, ha egy esetleg null értékű kifejezést használnak, ahol nem null értékre van szükség.
8.9.3 Null értékű referenciatípusok
Az űrlap T?
referenciatípusa (például string?
) null értékű hivatkozástípus. Egy null értékű változó alapértelmezett null állapota lehet null. A széljegyzet ?
azt a szándékot jelzi, hogy az ilyen típusú változók null értékűek. A fordító képes felismerni ezeket a szándékokat, hogy figyelmeztetéseket adjon ki. Ha a nullable annotációs környezet le van tiltva, ezzel a megjegyzéssel figyelmeztetést eredményezhet.
8.9.4 Null értékű környezet
8.9.4.1 Általános
A forráskód minden sora null értékű környezettel rendelkezik. A lefordíthatósági kontextus anotációinak és figyelmeztetéseinek jelzői a lefordíthatósági anotációk (§8.9.4.3) és a lefordíthatósági figyelmeztetések (§8.9.4.4) esetében. Minden jelölő engedélyezhető vagy letiltható. A fordítók statikus folyamatelemzéssel határozhatják meg a referenciaváltozók null állapotát. A referenciaváltozó null állapota (8.9.5. §) vagy nem null, lehet, hogy null, vagy alapértelmezett.
A null értékű környezet a forráskódban null értékű irányelveken (6.5.9. §) és/vagy a forráskódon kívüli implementációspecifikus mechanizmuson keresztül adható meg. Mindkét módszer használata esetén a null értékű irányelvek felülírják a külső mechanizmuson keresztül végrehajtott beállításokat.
A null értékű környezet alapértelmezett állapota implementáció által definiált.
Ebben a specifikációban az összes olyan C#-kódot, amely nem tartalmaz null értékű irányelveket, vagy amelyekről nem történik állítás a jelenlegi null értékű környezet állapotáról, feltételezzük, hogy null értékű környezet használatával lett lefordítva, ahol a széljegyzetek és a figyelmeztetések is engedélyezve vannak.
Jegyzet: Null értékű környezet, amelyben mindkét jelölő le van tiltva, megegyezik a referenciatípusok előző szokásos viselkedésével. végjegyzet
8.9.4.2 Null értékű letiltás
Ha a figyelmeztetés és a széljegyzetjelző is le van tiltva, a null értékű környezet le van tiltva.
Ha a null értékű környezet le van tiltva:
- Nem hozható létre figyelmeztetés, ha egy nem hitelesített referenciatípusú változó inicializálva van, vagy értékhez
null
van rendelve. - Nem hozható létre figyelmeztetés, ha egy hivatkozástípusú változó null értékkel rendelkezik.
- Bármely referenciatípus
T
esetében a jegyzet?
T?
egy üzenetet hoz létre, és a típusT?
megegyezik a következővelT
. - Bármilyen típusparaméter-korlátozás
where T : C?
esetén a jegyzet?
C?
egy üzenetet hoz létre, és a típusC?
megegyezik a következővelC
. - Bármilyen típusparaméter-korlátozás
where T : U?
esetén a jegyzet?
U?
egy üzenetet hoz létre, és a típusU?
megegyezik a következővelU
. - Az általános korlátozás
class?
figyelmeztető üzenetet hoz létre. A típusparaméternek referenciatípusnak kell lennie.Megjegyzés: Ez az üzenet a "figyelmeztetés" helyett "tájékoztató" jellegű, hogy ne tévessze össze a nem kapcsolódó null értékű figyelmeztetési beállítás állapotával. végjegyzet
- A null-megbocsátó operátornak
!
(12.8.9. §) nincs hatása.
Példa:
#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
záró példa
8.9.4.3 Null értékű széljegyzetek
Ha a figyelmeztető jelző le van tiltva, és a széljegyzetek jelölője engedélyezve van, a null értékű környezet széljegyzetek.
Ha a nullázható kontextus annotációk:
- Bármely referenciatípus
T
esetében a megjegyzés?
aztT?
jelzi, hogyT?
null értékű típus, míg a névtelenT
nem null értékű. - A rendszer nem generál nullabilitással kapcsolatos diagnosztikai figyelmeztetéseket.
- A nullát nem figyelembe vevő operátor
!
(12.8.9. §) módosíthatja az operandus nullás állapotát, és befolyásolhatja, hogy milyen diagnosztikai figyelmeztetések keletkeznek fordítási időpontban.
Példa:
#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
záró példa
8.9.4.4 Érvénytelen figyelmeztetések
Ha a figyelmeztető jelző engedélyezve van, és a széljegyzetek jelzője le van tiltva, a null értékű kontextus a következő: figyelmeztetések.
Ha a null értékű környezet figyelmeztetések, a fordító a következő esetekben hozhat létre diagnosztikát:
- Egy referenciaváltozót, amelyről megállapították, hogy esetleg null értékű, dereferenciálták.
- A nem null értékű referenciaváltozó egy olyan kifejezéshez van hozzárendelve, amely lehet null.
- Ez
?
egy null értékű hivatkozástípus megjegyzésére szolgál. - A null-megbocsátó operátor
!
(12.8.9. §) az operandus null állapotát nem null értékűre állítja.
Példa:
#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
záró példa
8.9.4.5 Nullable típusok engedélyezése
Ha a figyelmeztető jelző és a széljegyzetjelző is engedélyezve van, a null értékű környezet engedélyezve van.
Ha a null értékű környezet engedélyezve van:
- Bármely referenciatípus
T
esetében az annotáció?
a(z)T?
-benT?
-t nullázható típussá teszi, míg a nem jelöltT
nem nullázható. - A fordítók statikus folyamatelemzéssel határozhatják meg a referenciaváltozók null állapotát. Ha engedélyezve vannak a null értékű figyelmeztetések, a referenciaváltozó null állapota (8.9.5. §) lehet nem null értékű, esetleg null értékű, vagy esetleg alapértelmezett értékű, és
- A null-megbocsátó operátor
!
(12.8.9. §) az operandus null állapotát nem null értékűre állítja. - A fordítók figyelmeztetést adhatnak ki, ha egy típusparaméter nullsága nem egyezik meg a megfelelő típusargumentum nullképességével.
8.9.5 Nulla képességek és null állapotok
8.9.5.1 Általános
Nincs szükség arra, hogy a fordító statikus elemzéseket végezzen, és nincs szükség arra sem, hogy diagnosztikai figyelmeztetéseket generáljon a null értékűséggel kapcsolatban.
Az albekezdés hátralévő része feltételesen normatív.
8.9.5.2 Folyamatelemzés
A diagnosztikai figyelmeztetéseket generáló fordító megfelel ezeknek a szabályoknak.
Minden kifejezés három null állapotegyikével rendelkezik:
- talán null: A kifejezés értéke null értékű lehet.
- lehet, hogy alapértelmezett: A kifejezés értéke az adott típus alapértelmezett értékére értékelhető ki.
- not null: A kifejezés értéke nem null.
Egy kifejezés alapértelmezett null állapotát a típus határozza meg, a deklaráláskor pedig a széljegyzetek jelölőjének állapotát:
- Egy null értékű hivatkozástípus alapértelmezett null állapota a következő:
- Lehet, hogy null, ha a deklaráció olyan szövegben van, amelyben engedélyezve van a széljegyzetek jelzője.
- Nem null értékű, ha a deklaráció olyan szövegben van, amelyben a széljegyzetjelző le van tiltva.
- A nem null értékű hivatkozástípus alapértelmezett null állapota nem null.
Megjegyzés: A esetleg alapértelmezett állapotot akkor használjuk nem korlátozott típusparaméterekkel, amikor a típus egy nem-null értékű típus, mint például
string
, és a kifejezésdefault(T)
a null értéket veszi fel. Mivel a null érték nem része a nem null értékű típus tartományának, az állapot lehet alapértelmezett. végjegyzet
Diagnosztikát akkor lehet létrehozni, ha egy nem null értékű hivatkozástípusú változó (§9.2.1) inicializálva van, vagy egy olyan kifejezéshez van rendelve, amely null értékű lehet, ha a változót olyan szövegben deklarálják, amelyben engedélyezve van a széljegyzetjelző.
Példa: Fontolja meg a következő metódust, amelyben egy paraméter null értékű, és az érték nem null értékű típushoz van rendelve:
#nullable enable public class C { public void M(string? p) { // Warning: Assignment of maybe null value to non-nullable variable string s = p; } }
A fordító figyelmeztetést adhat ki, ha a null értékű paraméter egy olyan változóhoz van hozzárendelve, amelynek nem szabad null értékűnek lennie. Ha a paramétert a hozzárendelés előtt null értékűen ellenőrzik, a fordító ezt használhatja a null értékű állapotelemzésben, és nem ad ki figyelmeztetést:
#nullable enable public class C { public void M(string? p) { if (p != null) { string s = p; // No warning // Use s } } }
záró példa
A fordítók az elemzés részeként frissíthetik egy változó null állapotát.
Példa: A fordító a program bármely utasítása alapján módosíthatja az állapotot:
#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 }
Az előző példában a fordító dönthet úgy, hogy az utasítás
int length = p.Length;
után ap
null-állapota nem-null. Ha null értékű lenne, az az utasítás dobott volna egyNullReferenceException
. Ez hasonló a viselkedéshez, ha a kódot megelőzték volna azzal a kivétellelif (p == null) throw NullReferenceException();
, hogy az írott kód figyelmeztetést eredményezhet, amelynek célja, hogy figyelmeztessen arra, hogy implicit módon kivételt lehet kivenni. záró példa
A metódus későbbi részében a kód ellenőrzi, hogy s
nem null értékű-e a hivatkozás. A s
null-állapota null-ellenőrzött blokk zárása után esetleg null értékűvé változhat. A fordítók arra következtethetnek, hogy a s
lehet null, mert a kód azért lett megírva, hogy feltételezze, hogy null értékű lehetett. Ha a kód null értékű ellenőrzést tartalmaz, a fordítók általában arra következtethetnek, hogy az érték null értékű lehet:
példa: Az alábbi kifejezések mindegyike tartalmaz valamilyen null értékű ellenőrzést.
o
null állapota a következő utasítások után nem null állapotról talán null állapotra változhat.#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 } }
Az automatikus tulajdonság és a mezőszerű eseménydeklarációk egyaránt használnak fordító által létrehozott háttérmezőt. A null állapot elemzése arra következtethet, hogy az eseményhez vagy tulajdonsághoz való hozzárendelés egy fordító által létrehozott háttérmezőhöz való hozzárendelés.
Példa: A fordítók megállapíthatják, hogy egy automatikus tulajdonság vagy mezőszerű esemény írása a megfelelő fordító által létrehozott háttérmezőt írja. A tulajdonság null állapota megegyezik a háttérmező null állapotával.
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. } }
Az előző példában a konstruktor nem állítja be a
P
nem null értékre, és a fordító figyelmeztetést adhat ki. AP
tulajdonság elérésekor nincs figyelmeztetés, mert a tulajdonság típusa nem null értékű hivatkozástípus. záró példa
A fordítók egy tulajdonságot (§15.7) állapotváltozóként, vagy független be- és beállítási tartozékként (§15.7.3) kezelhetnek.
Példa: A fordító eldöntheti, hogy a tulajdonságba való írás megváltoztatja-e a tulajdonság olvasásának null állapotát, vagy ha egy tulajdonság olvasása megváltoztatja a tulajdonság null állapotát.
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 } } }
Az előző példában a háttérmező
DisappearingProperty
null értékűre van állítva olvasáskor. A fordító azonban feltételezheti, hogy egy tulajdonság olvasása nem változtatja meg a kifejezés null állapotát. záró példa
Egy fordító bármelyik kifejezést használhatja, ami egy változóra, tulajdonságra vagy eseményre hivatkozik, hogy a null állapotot nem null értékre állítsa. Ha null érték lenne, a hivatkozás értékének elérése kivételt okozott volna NullReferenceException
.
Példa:
public class C { private C? child; public void M() { _ = child.child.child; // Warning. Dereference possible null value var greatGrandChild = child.child.child; // No warning. } }
záró példa
8.9.5.3 Típusátalakítások
A diagnosztikai figyelmeztetéseket generáló fordító megfelel ezeknek a szabályoknak.
Jegyzet: A legfelső szintű vagy beágyazott nullbilitási széljegyzetek típusok közötti különbségei nem befolyásolják, hogy engedélyezett-e a típusok közötti átalakítás, mivel nincs szemantikai különbség a nem null értékű referenciatípus és annak megfelelő null értékű típusa között (8.9.1. §). végjegyzet
A fordító figyelmeztetést adhat ki, ha a nullbilitási széljegyzetek két típus között különböznek, akár legfelső szintű, akár beágyazott, amikor az átalakítás szűkül.
Példa: A felső szintű széljegyzetekben eltérő típusok
#nullable enable public class C { public void M1(string p) { _ = (string?)p; // No warning, widening } public void M2(string? p) { _ = (string)p; // Warning, narrowing _ = (string)p!; // No warning, suppressed } }
záró példa
Példa: A beágyazott nullbilitási széljegyzetekben eltérő típusok
#nullable enable public class C { public void M1((string, string) p) { _ = ((string?, string?))p; // No warning, widening } public void M2((string?, string?) p) { _ = ((string, string))p; // Warning, narrowing _ = ((string, string))p!; // No warning, suppressed } }
záró példa
A fordítók követhetik a felületi varianciára (18.2.3.3.3.§), a delegált varianciára (20.4. §) és a tömb kovarianciára (17.6. §) vonatkozó szabályokat annak meghatározására, hogy a típuskonverziókra figyelmeztetést kell-e kiadni.
#nullable enable public class C { public void M1(IEnumerable<string> p) { IEnumerable<string?> v1 = p; // No warning } public void M2(IEnumerable<string?> p) { IEnumerable<string> v1 = p; // Warning IEnumerable<string> v2 = p!; // No warning } public void M3(Action<string?> p) { Action<string> v1 = p; // No warning } public void M4(Action<string> p) { Action<string?> v1 = p; // Warning Action<string?> v2 = p!; // No warning } public void M5(string[] p) { string?[] v1 = p; // No warning } public void M6(string?[] p) { string[] v1 = p; // Warning string[] v2 = p!; // No warning } }
záró példa
A fordító figyelmeztetést adhat ki, ha a nullability mindkét irányban eltér olyan típusok esetében, amelyek nem teszik lehetővé a változatkonverziót.
#nullable enable public class C { public void M1(List<string> p) { List<string?> v1 = p; // Warning List<string?> v2 = p!; // No warning } public void M2(List<string?> p) { List<string> v1 = p; // Warning List<string> v2 = p!; // No warning } }
záró példa
Feltételesen normatív szöveg vége
ECMA C# draft specification