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.
6.1 Programok
A C#-programok egy vagy több forrásfájlból állnak, más néven fordítási egységekből (14.2.§). Bár egy fordítási egység egy-az-egyhez levelezéssel rendelkezhet egy fájlrendszerbeli fájllal, az ilyen levelezésre nincs szükség.
Elméletileg a program három lépésből áll:
- Átalakítás, amely egy adott karakterrepertoárból és kódolási sémából konvertál egy fájlt Unicode-karakterek sorozatává.
- Lexikális elemzés, amely unicode bemeneti karakterek adatfolyamát tokenek adatfolyamává fordítja le.
- A szintaktikai elemzés olyan folyamat, amely a tokenek adatfolyamát végrehajtható kódra fordítja.
A megfelelő implementációknak el kell fogadniuk az UTF-8 kódolási formával kódolt Unicode-fordítási egységeket (a Unicode szabványnak megfelelően), és Unicode-karakterek sorozatává kell alakítaniuk őket. A implementációk további karakterkódolási sémákat (például UTF-16, UTF-32 vagy nem Unicode-karakterleképezéseket) is elfogadhatnak és átalakíthatnak.
Megjegyzés: A Unicode NULL karakter (U+0000) kezelése implementációfüggő. Erősen ajánlott, hogy a fejlesztők ne használják ezt a karaktert a forráskódjukban a hordozhatóság és az olvashatóság érdekében. Ha a karaktert karakter- vagy sztringkonstansban kell megadni, a feloldósorozatok
\0
vagy\u0000
használhatók helyette. végjegyzet
Megjegyzés: A specifikáció hatókörén kívül esik annak meghatározása, hogy a Unicode-tól eltérő karakterábrázolást használó fájlok hogyan alakíthatók át Unicode-karakterek sorozatává. Az ilyen átalakítás során azonban azt javasoljuk, hogy a másik karakterkészlet szokásos sorelválasztó karakterét (vagy sorozatát) fordítsuk le a kétkarakterű sorozatra, amely a Unicode kocsivissza karakterből (U+000D) és a Unicode sorelváltási karakterből (U+000A) áll. Ennek az átalakításnak többnyire nincsenek látható hatásai; ez azonban hatással lesz a szó szerinti sztring literális tokenek értelmezésére (§6.4.5.6). Ennek a javaslatnak az a célja, hogy lehetővé tegye a szó szerinti karakterlánc literálnak, hogy ugyanazt a karaktersorozatot hozza létre, amikor a fordítási egység átkerül olyan rendszerek között, amelyek különböző, nem Unicode karakterkészleteket támogatnak, különösen eltérő karaktersorozatokat használnak a sorelválasztáshoz. végjegyzet
6.2 Nyelvtanok
6.2.1 Általános
Ez a specifikáció a C# programozási nyelv szintaxisát mutatja be két nyelvtan használatával. A lexikális nyelvhelyesség (6.2.3. §) meghatározza, hogy a Unicode-karakterek hogyan lesznek egyesítve a sorkijelölők, a szabad terület, a megjegyzések, a jogkivonatok és az előfeldolgozási irányelvek formájában. A szintaktikai nyelvtan (§6.2.4) határozza meg, hogy a lexikális nyelvtanból származó jogkivonatok hogyan lesznek kombinálva C#-programok formájában.
Minden terminálkaraktert az U+0020 és U+007F tartomány megfelelő Unicode-karaktereként kell értelmezni, szemben a többi Unicode-karaktertartomány hasonló megjelenésű karakterével.
6.2.2 Nyelvtani jelölés
A lexikális és szintaktikai nyelvtanokat az ANTLR nyelvhelyességi eszköz Kiterjesztett Backus-Naur formájában mutatjuk be.
Bár az ANTLR jelölést használja, ez a specifikáció nem tartalmaz teljes ANTLR-kész "referencia-nyelvtant" a C#-hoz; a lexer és elemző írása kézzel vagy egy olyan eszköz használatával, mint az ANTLR, nem tartozik a nyelvi specifikáció hatókörébe. Ezzel a minősítéssel ez a specifikáció megpróbálja minimalizálni a megadott nyelvtan és az ANTLR-ben történő lexer és elemző létrehozásához szükséges közötti rést.
Az ANTLR különbséget tesz lexikális és szintaktikai (az ANTLR által elemzőnek nevezett) nyelvtanok között azáltal, hogy a lexikális szabályokat nagybetűvel, az elemzési szabályokat pedig kisbetűvel kezdi a jelölésében.
Megjegyzés: A C#-lexikális nyelvtan (6.2.3.§) és a szintaktikai nyelvhelyesség (6.2.4. §) nem egyezik meg pontosan az ANTLR lexikális és elemző nyelvtanokra való felosztásával. Ez a kis eltérés azt jelenti, hogy néhány ANTLR-elemző szabályt használnak a C#-lexikális nyelvhelyesség megadásakor. végjegyzet
6.2.3 Lexikális nyelvtan
A C# lexikális nyelvtana a 6.3, 6.4 és 6.5. A lexikális nyelvhelyesség terminálszimbóluma a Unicode karakterkészlet karakterei, a lexikális nyelvhelyesség pedig azt határozza meg, hogy a karakterek hogyan lesznek egyesítve jogkivonatokké (6.4.§), szóközökké (6.3.4.§), megjegyzésekhez (6.3.3.3. §) és előfeldolgozási irányelvekhez (6.5. §).
A szintaktikai nyelvtan számos terminálszimbóluma nincs explicit módon tokenekként definiálva a lexikális nyelvtanban. Az ANTLR viselkedését előnyösen használják ki, mivel a grammatikában lévő szó szerinti karakterláncok implicit lexikális tokenekként kivonásra kerülnek; ez lehetővé teszi, hogy a kulcsszavak, operátorok stb. a grammatikában a tokennév helyett a szó szerinti ábrázolásukkal legyenek ábrázolva.
A C#-programokban minden fordítási egységnek meg kell felelnie a lexikális nyelvhelyesség bemeneti előállításának (6.3.1. §).
6.2.4 Szintaktikai nyelvtan
A C# szintaktikai nyelvtanát az alklámot követő záradékok, alklámok és mellékletek mutatják be. A szintaktikai nyelvtan terminálszimbólumai a lexikális nyelvtan által explicit módon meghatározott tokenek és implicit módon a nyelvtanban szereplő literálok (§6.2.3). A szintaktikai nyelvtan határozza meg, hogy a tokenek hogyan kombinálódnak C# programokká.
A C#-programok minden fordítási egységének meg kell felelnie a szintaktikai nyelvhelyesség compilation_unit szabályának (14.2. §).
6.2.5 Nyelvtani kétértelműségek
A következőhöz tartozó produkciók:
- simple_name (12.8.4. §),
- member_access (§ 12.8.7),
- null_conditional_member_access (§ 12.8.8)
- dependent_access (12.8.8. §)
- base_access (12.8.15. §) és
- pointer_member_access (§23.6.3);
(a "egyértelmű produkciók") kétértelműségeket eredményezhetnek a kifejezések nyelvtanában.
Ezek az előállítások olyan szituációkban fordulnak elő, ahol egy kifejezésben érték fordulhat elő, és egy vagy több alternatívát kínálnak, amelyek a nyelvtani szerkezettel "identifier type_argument_list?
" végződnek. Ez az opcionális type_argument_list , amely a lehetséges kétértelműséget eredményezi.
Példa: Az utasítás:
F(G<A, B>(7));
F
két argumentummal,G < A
-val ésB > (7)
-vel történő hívásként értelmezhető. Másik lehetőségkéntF
egy argumentummal hívható meg, amely egy általános metódusG
meghívása két típusargumentummal és egy normál argumentummal.záró példa
Ha egy tokenek sorozata kontextusban elemezhető, mint az egyik egyértelműsített produkció, beleértve egy választható type_argument_list (§8.4.2), akkor meg kell vizsgálni a záró >
tokent közvetlenül követő tokent, és ha igen:
- az egyik ;
( ) ] } : ; , . ? == != | ^ && || & [
vagy - az egyik relációs operátor
< <= >= is as
; vagy - egy környezeti lekérdezési kulcsszó jelenik meg egy lekérdezési kifejezésben.
akkor a type_argument_list a pontosított produkció részeként meg kell őrizni, és a tokenek sorrendjének bármely más lehetséges értelmezését el kell vetni. Ellenkező esetben a type_argument_list tokenek nem tekinthetők az egyértelműsített kifejezés részének, még akkor sem, ha a tokeneknek nincs más lehetséges elemzése.
Megjegyzés: Ezek a egyértelműsítési szabályok nem alkalmazhatók más produkciók elemzésekor, még akkor sem, ha hasonló végződésű "
identifier type_argument_list?
"-ként végződnek; az ilyen produkciókat a szokásos módon kell elemezni. Példák: namespace_or_type_name (§7.8); named_entity (§12.8.23); null_conditional_projection_initializer (§12.8.8); és qualified_alias_member (§14.8.1). végjegyzet
Példa: Az utasítás:
F(G<A, B>(7));
a szabály szerint egy argumentummal rendelkező hívásként
F
lesz értelmezve, amely egy általános metódusG
hívása két típusargumentummal és egy normál argumentummal. A kijelentésekF(G<A, B>7); F(G<A, B>>7);
A
F
hívásként lesz értelmezve két argumentummal. A kijelentésx = F<A> + y;
A kifejezés kisebb, nagyobb és unáris plusz operátorként lesz értelmezve, mintha így lett volna írva:
x = (F < A) > (+y)
, nem pedig mint egy simple_name egy type_argument_list, amelyet egy bináris plusz operátor követ. A nyilatkozatbanx = y is C<T> && z;
a tokent
C<T>
a namespace_or_type_name a type_argument_list néven értelmezi, mivel a type_argument_list után megjelenik az egyértelműsítő token&&
.A kifejezés
(A < B, C > D)
egy kételemű tömb, ahol mindkét elem egy-egy összehasonlítás.A kifejezés
(A<B,C> D, E)
két elemet tartalmazó rekord, az első egy deklarációs kifejezés.A meghívásnak
M(A < B, C > D, E)
három argumentuma van.A meghívásnak
M(out A<B,C> D, E)
két argumentuma van, amelyek közül az első egyout
deklaráció.A kifejezés
e is A<B> C
deklarációs mintát használ.Az esetcímke
case A<B> C:
deklarációs mintát használ.záró példa
Relational_expression felismerésekor (§12.12.1), ha a "relational_expressionis
típus" és a "relational_expressionis
minta" alternatívák is alkalmazhatók, és a típus elérhető típusra oldható fel, akkor a "relational_expressionis
típus" alternatívát kell választani.
6.3 Lexikális elemzés
6.3.1 Általános
Az egyszerűség kedvéért a lexikai nyelvtan a következő elnevezett lexer tokeneket határozza meg és használ hivatkozásként:
DEFAULT : 'default' ;
NULL : 'null' ;
TRUE : 'true' ;
FALSE : 'false' ;
ASTERISK : '*' ;
SLASH : '/' ;
Bár ezek lexer szabályok, ezek a nevek nagybetűs betűkkel vannak beírva, hogy megkülönböztessék őket a szokásos lexer szabálynevektől.
Megjegyzés: Ezek a kényelmi szabályok kivételt jelentenek a literal stringek által definiált tokenek explicit tokenneveinek megadásának szokásos gyakorlattól eltérően. végjegyzet
A bemeneti termelés határozza meg a C#-fordítási egység lexikális szerkezetét.
input
: input_section?
;
input_section
: input_section_part+
;
input_section_part
: input_element* New_Line
| PP_Directive
;
input_element
: Whitespace
| Comment
| token
;
Megjegyzés: A fenti nyelvtant az ANTLR elemzési szabályai írják le, a C#-fordítási egység lexikális szerkezetét határozza meg, nem lexikális tokeneket. végjegyzet
A C#-fordítási egység lexikális szerkezetét öt alapvető elem alkotja: Sorkijelölők (6.3.2.§), szóköz (6.3.4.§), megjegyzések (6.3.3.§), jogkivonatok (6.4. §) és előfeldolgozási irányelvek (6.5. §). Ezen alapvető elemek közül csak a tokenek jelentősek egy C#-program szintaktikai nyelvtanában (§6.2.4).
A C#-fordítási egység lexikális feldolgozása abból áll, hogy a fájlt tokenek sorozatává csökkenti, amely a szintaktikai elemzés bemenetévé válik. A sorvégjelek, a szóközök és a megjegyzések a tokenek elválasztására szolgálnak, az előfeldolgozási utasítások pedig a fordítási egység bizonyos részeinek kihagyását okozhatják, de ettől eltekintve ezek a lexikális elemek nem befolyásolják egy C# program szintaktikai szerkezetét.
Ha több lexikális nyelvtani produkció egy fordítási egységben szereplő karaktersorozatnak felel meg, a lexikális feldolgozás mindig a lehető leghosszabb lexikális elemet alkotja.
Példa: A karakterlánc
//
egy egysoros megjegyzés kezdeteként van feldolgozva, mert ez a lexikális elem hosszabb egyetlen/
tokennél. záró példa
Egyes tokeneket lexikális szabályok határoznak meg; főszabály és egy vagy több alszabály. Az utóbbiak a nyelvtanban fragment
azért vannak így megjelölve, hogy a szabály egy másik elem egy részét határozza meg. A töredékszabályok nem tekinthetők a lexikális szabályok felülről lefelé rendezett sorrendjének.
Megjegyzés: Az ANTLR
fragment
egy olyan kulcsszó, amely az itt definiált viselkedést hozza létre. végjegyzet
6.3.2 Sorelválasztók
A sorvégi jelek a C#-fordítási egység karaktereit sorokra osztják.
New_Line
: New_Line_Character
| '\u000D\u000A' // carriage return, line feed
;
A forráskódszerkesztő eszközökkel való kompatibilitás érdekében, amelyek fájlvégpont-jelölőket adnak hozzá, és lehetővé teszik, hogy a fordítási egység megfelelően megszakított sorok sorozataként legyen megtekinthető, a következő átalakítások lesznek alkalmazva egy C#-program összes fordítási egységére:
- Ha a fordítási egység utolsó karaktere egy Control-Z karakter (U+001A), akkor a rendszer törli a karaktert.
- Egy kocsivissza karakter (U+000D) hozzáadódik a fordítási egység végéhez, ha a fordítási egység nem üres, és a fordítási egység utolsó karaktere nem kocsivissza (U+000D), soremelés (U+000A), következő sor karakter (U+0085), vonalelválasztó (U+2028) vagy bekezdéselválasztó (U+2029).
Megjegyzés: A további kocsivisszatérítés karakter lehetővé teszi, hogy a program egy PP_Directive-vel végződjön (§6.5), amely nem tartalmaz New_Line-t. végjegyzet
6.3.3 Megjegyzések
A megjegyzések két formája támogatott: a tagolt megjegyzések és az egysoros megjegyzések.
A tagolt megjegyzések a karakterekkel /*
kezdődnek, és a karakterekkel */
végződnek. A tagolt megjegyzések egy sor, egy sor vagy több sor egy részét foglalhatják el.
Példa: A példa
/* Hello, world program This program writes "hello, world" to the console */ class Hello { static void Main() { System.Console.WriteLine("hello, world"); } }
tagolt megjegyzést tartalmaz.
záró példa
Az egysoros megjegyzés a karakterekkel //
kezdődik, és a sor végéig terjed.
Példa: A példa
// Hello, world program // This program writes "hello, world" to the console // class Hello // any name will do for this class { static void Main() // this method must be named "Main" { System.Console.WriteLine("hello, world"); } }
több egysoros megjegyzést jelenít meg.
záró példa
Comment
: Single_Line_Comment
| Delimited_Comment
;
fragment Single_Line_Comment
: '//' Input_Character*
;
fragment Input_Character
// anything but New_Line_Character
: ~('\u000D' | '\u000A' | '\u0085' | '\u2028' | '\u2029')
;
fragment New_Line_Character
: '\u000D' // carriage return
| '\u000A' // line feed
| '\u0085' // next line
| '\u2028' // line separator
| '\u2029' // paragraph separator
;
fragment Delimited_Comment
: '/*' Delimited_Comment_Section* ASTERISK+ '/'
;
fragment Delimited_Comment_Section
: SLASH
| ASTERISK* Not_Slash_Or_Asterisk
;
fragment Not_Slash_Or_Asterisk
: ~('/' | '*') // Any except SLASH or ASTERISK
;
A megjegyzések nem ágyaznak be. A karaktersorozatok /*
, és */
nincs különleges jelentésük egy egysoros megjegyzésen belül, és a karaktersorozatok //
, és /*
nincs különleges jelentésük egy elhatárolt megjegyzésen belül.
A megjegyzések nem dolgozhatók fel karakter- és sztringkonstansokon belül.
Megjegyzés: Ezeket a szabályokat gondosan kell értelmezni. Az alábbi példában, például az elválasztott megjegyzés, amely mielőtt
A
kezdődik,B
ésC()
között végződik. Ennek az az oka, hogy// B */ C();
valójában nem egysoros megjegyzés, mivel
//
nincs különleges jelentése egy elhatárolt megjegyzésen belül, és így*/
a szokásos különleges jelentése is van ebben a sorban.Hasonlóképpen, a tagolt megjegyzés, amely
D
előtt kezdődik, aE
előtt végződik. Ennek az az oka, hogy"D */ "
valójában nem karakterlánc literál, mivel a kezdeti dupla idézőjel egy határolt megjegyzésben jelenik meg.Az egy soros megjegyzésekben a
/*
és*/
elemeknek nincs különleges jelentése, ezért egy forráskód blokk sorait úgy lehet megjegyzésekké alakítani, hogy mindegyik sor elejére//
kerül. Általában nem érdemes ezeket a sorokat és/*
utánuk elhelyezni*/
, mivel ez nem foglalja össze megfelelően az elhatárolt megjegyzéseket a blokkban, és általában teljesen megváltoztathatja az ilyen elhatárolt megjegyzések szerkezetét.Példakód:
static void Main() { /* A // B */ C(); Console.WriteLine(/* "D */ "E"); }
végjegyzet
Single_Line_Commentek és Delimited_Commentek meghatározott formátumai használhatók dokumentációs megjegyzésekként, a §D-ben leírtak szerint.
6.3.4 Szabad terület
Az üres terület olyan karakterek összességeként van meghatározva, amelyek a Unicode Zs osztályába tartoznak (ideértve a szóköz karaktert), valamint a vízszintes tabulátor karaktert, a függőleges tabulátor karaktert és a lapdobás karaktert.
Whitespace
: [\p{Zs}] // any character with Unicode class Zs
| '\u0009' // horizontal tab
| '\u000B' // vertical tab
| '\u000C' // form feed
;
6.4 Tokenek
6.4.1 Általános
Többféle token létezik: azonosítók, kulcsszavak, literálok, operátorok és szimbólumok. A szóközök és a megjegyzések nem tokenek, de elválasztóként szolgálnak a tokenek között.
token
: identifier
| keyword
| Integer_Literal
| Real_Literal
| Character_Literal
| String_Literal
| operator_or_punctuator
;
Megjegyzés: Ez egy ANTLR-elemző szabály, amely nem definiál lexikális tokent, hanem a tokenfajták gyűjteményét. végjegyzet
6.4.2 Unicode karakter escape szekvenciák
A Unicode feloldósorozat egy Unicode kódpontot jelöl. A Unicode-feloldósorozatok feldolgozása azonosítókban (6.4.3.§), karakterkonstansokban (6.4.5.5.5.5. §), reguláris sztringkonstansokban (6.4.5.6. §) és interpolált reguláris sztringkifejezésekben történik (12.8.3. §). A Unicode-feloldósorozatok más helyen nem dolgozhatók fel (például operátort, írásjelet vagy kulcsszót alkotnak).
fragment Unicode_Escape_Sequence
: '\\u' Hex_Digit Hex_Digit Hex_Digit Hex_Digit
| '\\U' Hex_Digit Hex_Digit Hex_Digit Hex_Digit
Hex_Digit Hex_Digit Hex_Digit Hex_Digit
;
A Unicode-karakterek feloldási sorozata az "\u" vagy "\U" karaktereket követő hexadecimális szám által létrehozott egyetlen Unicode-kódpontot jelöli. Mivel a C# karakter- és sztringértékekben a Unicode kódpontok 16 bites kódolását használja, a tartomány U+10000
U+10FFFF
egy Unicode kódpontja két Unicode helyettesítő kódegység használatával jelenik meg. A fenti U+FFFF
Unicode-kódpontok karakterkonstansokban nem engedélyezettek. A fenti U+10FFFF
Unicode-kódpontok érvénytelenek, és nem támogatottak.
A rendszer nem végez több fordítást. Például a "\u005Cu005C"
sztringkonstans egyenértékű a "\u005C"
és nem a "\"
elemmel.
Megjegyzés: A Unicode értéke
\u005C
a "\
" karakter. végjegyzet
Példa: A példa
class Class1 { static void Test(bool \u0066) { char c = '\u0066'; if (\u0066) { System.Console.WriteLine(c.ToString()); } } }
"
\u0066
többféle használati módot mutat be, amely a „f
” betű menekülési sorozatát jelenti." A program egyenértékű aclass Class1 { static void Test(bool f) { char c = 'f'; if (f) { System.Console.WriteLine(c.ToString()); } } }
záró példa
6.4.3 Azonosítók
Az ebben az alklámban megadott azonosítókra vonatkozó szabályok pontosan megfelelnek a Unicode szabvány 15. mellékletében javasoltaknak, azzal a kivételével, hogy az aláhúzás kezdeti karakterként engedélyezett (a C programozási nyelvhez hasonlóan), az azonosítókban Unicode-feloldósorozatok engedélyezettek, a "@
" karakter pedig előtagként, hogy a kulcsszavakat azonosítóként lehessen használni.
identifier
: Simple_Identifier
| contextual_keyword
;
Simple_Identifier
: Available_Identifier
| Escaped_Identifier
;
fragment Available_Identifier
// excluding keywords or contextual keywords, see note below
: Basic_Identifier
;
fragment Escaped_Identifier
// Includes keywords and contextual keywords prefixed by '@'.
// See note below.
: '@' Basic_Identifier
;
fragment Basic_Identifier
: Identifier_Start_Character Identifier_Part_Character*
;
fragment Identifier_Start_Character
: Letter_Character
| Underscore_Character
;
fragment Underscore_Character
: '_' // underscore
| '\\u005' [fF] // Unicode_Escape_Sequence for underscore
| '\\U0000005' [fF] // Unicode_Escape_Sequence for underscore
;
fragment Identifier_Part_Character
: Letter_Character
| Decimal_Digit_Character
| Connecting_Character
| Combining_Character
| Formatting_Character
;
fragment Letter_Character
// Category Letter, all subcategories; category Number, subcategory letter.
: [\p{L}\p{Nl}]
// Only escapes for categories L & Nl allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Combining_Character
// Category Mark, subcategories non-spacing and spacing combining.
: [\p{Mn}\p{Mc}]
// Only escapes for categories Mn & Mc allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Decimal_Digit_Character
// Category Number, subcategory decimal digit.
: [\p{Nd}]
// Only escapes for category Nd allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Connecting_Character
// Category Punctuation, subcategory connector.
: [\p{Pc}]
// Only escapes for category Pc allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Formatting_Character
// Category Other, subcategory format.
: [\p{Cf}]
// Only escapes for category Cf allowed, see note below.
| Unicode_Escape_Sequence
;
Megjegyzés:
- A fent említett Unicode-karakterosztályokról a Unicode Standard című témakörben olvashat bővebben.
- A töredék Available_Identifier megköveteli a kulcsszavak és a környezetfüggő kulcsszavak kizárását. Ha a specifikáció nyelvtanát ANTLR-vel dolgozzák fel, akkor ezt a kizárást automatikusan kezeli az ANTLR szemantikája:
- A kulcsszavak és a környezetfüggő kulcsszavak a nyelvtanban szószerinti karakterláncként fordulnak elő.
- Az ANTLR implicit lexikai token szabályokat hoz létre ezekből a literálokból.
- Az ANTLR ezeket az implicit szabályokat a nyelvtan explicit lexikális szabályai előtt veszi figyelembe.
- Ezért a Available_Identifier töredék nem egyezik meg a kulcsszavakkal vagy a környezetfüggő kulcsszavakkal, mivel azok lexikális szabályai megelőzik.
- A töredék Escaped_Identifier magában foglalja az escape-el jelölt kulcsszavakat és kontextuális kulcsszavakat, mivel ezek egy hosszabb tokent alkotnak, amely egy karakterrel kezdődik, és a lexikai feldolgozás mindig a lehető leghosszabb lexikai elemet formálja meg (§6.3.1).
- Megvalósítási probléma, hogy egy implementáció hogyan kényszeríti ki az engedélyezett Unicode_Escape_Sequence értékekre vonatkozó korlátozásokat.
végjegyzet
Példa: Az érvényes azonosítók például a
identifier1
,_identifier2
és@if
. záró példa
A megfelelő programokban az azonosítónak a Unicode Normalization Form C által definiált kanonikus formátumban kell lennie, a Unicode Standard Melléklet 15 szerint. A C normalizálási űrlapon nem szereplő azonosítók észlelésekor a viselkedés implementációban van definiálva; azonban nincs szükség diagnosztikára.
A "@
" előtag lehetővé teszi a kulcsszavak azonosítóként való használatát, ami akkor hasznos, ha más programozási nyelvekkel együttműködik. A karakter @
valójában nem része az azonosítónak, ezért az azonosító más nyelveken normál azonosítóként, az előtag nélkül is látható. Az előtaggal @
rendelkező azonosítót verbatim azonosítónak nevezzük.
Megjegyzés: A
@
előtag használata az olyan azonosítóknál, amelyek nem kulcsszavak, engedélyezett, de stílusbeli okokból erősen nem javasolt. végjegyzet
Példa: A példa:
class @class { public static void @static(bool @bool) { if (@bool) { System.Console.WriteLine("true"); } else { System.Console.WriteLine("false"); } } } class Class1 { static void M() { cl\u0061ss.st\u0061tic(true); } }
egy "
class
" nevű osztályt határoz meg egy "static
" nevű statikus metódussal, amely egy "bool
" nevű paramétert használ. Vegye figyelembe, hogy mivel a Unicode escape-ek nem engedélyezettek a kulcsszavakban, a "cl\u0061ss
" token egy azonosító, és ugyanaz az azonosító, mint a "@class
".záró példa
Két azonosító azonosnak minősül, ha az alábbi átalakítások alkalmazása után azonosak, sorrendben:
- Ha használatban van, a "
@
" előtag eltávolításra kerül. - Minden Unicode_Escape_Sequence a megfelelő Unicode-karakterévé alakul át.
- Minden Formatting_Characterel lesz távolítva.
Az elnevezett _
azonosító szemantikája attól függ, hogy milyen környezetben jelenik meg:
- Megnevezett programelemet jelölhet, például változót, osztályt vagy metódust vagy
- A kidobás jelölésére használható (§9.2.9.2).
A két egymást követő aláhúzásjelet (U+005F
) tartalmazó azonosítók a implementáció számára vannak fenntartva; ilyen azonosító definiálása esetén azonban nincs szükség diagnosztikára.
Megjegyzés: Előfordulhat például, hogy egy implementáció kiterjesztett kulcsszavakat biztosít, amelyek két aláhúzással kezdődnek. végjegyzet
6.4.4 Kulcsszavak
A kulcsszó egy azonosítóhoz hasonló karakterlánc, amely fenntartott, és nem használható azonosítóként, kivéve ha a @
karakter áll előtte.
keyword
: 'abstract' | 'as' | 'base' | 'bool' | 'break'
| 'byte' | 'case' | 'catch' | 'char' | 'checked'
| 'class' | 'const' | 'continue' | 'decimal' | DEFAULT
| 'delegate' | 'do' | 'double' | 'else' | 'enum'
| 'event' | 'explicit' | 'extern' | FALSE | 'finally'
| 'fixed' | 'float' | 'for' | 'foreach' | 'goto'
| 'if' | 'implicit' | 'in' | 'int' | 'interface'
| 'internal' | 'is' | 'lock' | 'long' | 'namespace'
| 'new' | NULL | 'object' | 'operator' | 'out'
| 'override' | 'params' | 'private' | 'protected' | 'public'
| 'readonly' | 'ref' | 'return' | 'sbyte' | 'sealed'
| 'short' | 'sizeof' | 'stackalloc' | 'static' | 'string'
| 'struct' | 'switch' | 'this' | 'throw' | TRUE
| 'try' | 'typeof' | 'uint' | 'ulong' | 'unchecked'
| 'unsafe' | 'ushort' | 'using' | 'virtual' | 'void'
| 'volatile' | 'while'
;
A környezetfüggő kulcsszó egy azonosítószerű karaktersorozat, amely bizonyos kontextusokban különleges jelentéssel rendelkezik, de nem foglalt, és ezen környezeteken kívül, valamint a @
karakter előtagjaként is használható.
contextual_keyword
: 'add' | 'alias' | 'ascending' | 'async' | 'await'
| 'by' | 'descending' | 'dynamic' | 'equals' | 'from'
| 'get' | 'global' | 'group' | 'into' | 'join'
| 'let' | 'nameof' | 'notnull' | 'on' | 'orderby'
| 'partial' | 'remove' | 'select' | 'set' | 'unmanaged'
| 'value' | 'var' | 'when' | 'where' | 'yield'
;
Megjegyzés: A kulcsszó és contextual_keyword elemzői szabályok, mivel nem vezetnek be új token-típusokat. Az összes kulcsszót és a környezetfüggő kulcsszavakat implicit lexikális szabályok határozzák meg, amikor a nyelvtanban literális sztringekként fordulnak elő (6.2.3.§). végjegyzet
A legtöbb esetben a környezetfüggő kulcsszavak szintaktikai helye olyan, hogy soha nem téveszthető össze a szokásos azonosítóhasználattal. Egy tulajdonságdeklaráción belül például az és get
az set
azonosítók különleges jelentéssel rendelkeznek (15.7.3. §). Ezeken a helyeken get
vagy set
kivételével soha nem megengedett más azonosító, így ezek használata nem ütközik az ezen szavak azonosítóként való alkalmazásával.
Bizonyos esetekben a nyelvhelyesség nem elegendő a környezeti kulcsszóhasználat és az azonosítók megkülönböztetéséhez. Minden ilyen esetben meg lesz adva, hogyan lehet egyértelműsíteni a kettőt. Az implicit módon beírt helyi változódeklarációkban (var
) például a környezetfüggő kulcsszó ütközhet egy deklarált var
típussal, amely esetben a deklarált név elsőbbséget élvez az azonosító környezetfüggő kulcsszóként való használatával szemben.
Egy másik ilyen egyértelműsítés például a környezetfüggő kulcsszó await
(12.9.8.1. §), amelyet csak akkor tekintünk kulcsszónak, ha egy deklarált async
metóduson belül van, de máshol azonosítóként is használható.
A kulcsszavakhoz hasonlóan a környezetfüggő kulcsszavak is használhatók szokásos azonosítóként a karakterrel való előtagolásukkal @
.
Megjegyzés: Ha környezetfüggő kulcsszavakként használják, ezek az azonosítók nem tartalmazhatnak Unicode_Escape_Sequences-t. végjegyzet
6.4.5 Literálok
6.4.5.1 Általános
A literál (12.8.2. §) egy érték forráskód-ábrázolása.
literal
: boolean_literal
| Integer_Literal
| Real_Literal
| Character_Literal
| String_Literal
| null_literal
;
Megjegyzés: A literál egy elemzőszabály, mivel más tokentípusokat csoportosít, és nem vezet be új tokentípust. végjegyzet
6.4.5.2 Logikai literálok
Két logikai literálérték van: true
és false
.
boolean_literal
: TRUE
| FALSE
;
Megjegyzés: boolean_literal egy elemző szabály, mivel más jogkivonattípusokat csoportosít, és nem vezet be új jogkivonattípust. végjegyzet
A "boolean_literal" típusa bool
.
6.4.5.3 Egész szám literálok
Az egész számkonstansok a int
, uint
, long
, és ulong
típusú értékek írására szolgálnak. Az egész számkonstansnak három lehetséges formája van: decimális, hexadecimális és bináris.
Integer_Literal
: Decimal_Integer_Literal
| Hexadecimal_Integer_Literal
| Binary_Integer_Literal
;
fragment Decimal_Integer_Literal
: Decimal_Digit Decorated_Decimal_Digit* Integer_Type_Suffix?
;
fragment Decorated_Decimal_Digit
: '_'* Decimal_Digit
;
fragment Decimal_Digit
: '0'..'9'
;
fragment Integer_Type_Suffix
: 'U' | 'u' | 'L' | 'l' |
'UL' | 'Ul' | 'uL' | 'ul' | 'LU' | 'Lu' | 'lU' | 'lu'
;
fragment Hexadecimal_Integer_Literal
: ('0x' | '0X') Decorated_Hex_Digit+ Integer_Type_Suffix?
;
fragment Decorated_Hex_Digit
: '_'* Hex_Digit
;
fragment Hex_Digit
: '0'..'9' | 'A'..'F' | 'a'..'f'
;
fragment Binary_Integer_Literal
: ('0b' | '0B') Decorated_Binary_Digit+ Integer_Type_Suffix?
;
fragment Decorated_Binary_Digit
: '_'* Binary_Digit
;
fragment Binary_Digit
: '0' | '1'
;
Az egész szám literáljának típusa a következőképpen van meghatározva:
- Ha a literálnak nincs utótagja, akkor az első ilyen típusú, amelyben az érték ábrázolható:
int
,uint
, ,long
ulong
. - Ha a literál utótagja
U
vagyu
, akkor rendelkezik azzal az első típussal, amelyben az értéke ábrázolható:uint
,ulong
. - Ha a literál
L
vagyl
utótaggal rendelkezik, akkor az első olyan típus, amelyben az értéke ábrázolható, a következő:long
,ulong
. - Ha a literál utótagja
UL
,Ul
,uL
,ul
,LU
,Lu
,lU
, vagylu
, akkorulong
típusú.
Ha egy egész számkonstans által képviselt érték kívül esik a ulong
típus tartományán, fordítási időhiba lép fel.
Megjegyzés: Stílusbeli ajánlásként azt javasoljuk, hogy a „
L
” használata helyett inkább a „l
” literált használja, amikorlong
típusú literálokat ír, mivel a „l
” betű könnyen összetéveszthető a „1
” számmal. végjegyzet
Annak érdekében, hogy a int
és long
legkisebb lehetséges értékei egész számkonstansként írása történhessen, a következő két szabály létezik:
- Amikor egy Integer_Literal ábrázolja az
2147483648
(2³¹) értéket, és az Integer_Type_Suffix nem jelenik meg közvetlenül az unáris mínusz operátor után (§12.9.3), akkor az eredmény (mindkét token) egy konstans int típusú, amelynek értéke−2147483648
(−2³¹). Minden más helyzetben az ilyen Integer_Literaluint
típusú. - Ha egy Integer_Literal az értéket
9223372036854775808
(2⁶³) jelöli, és nincs Integer_Type_Suffix vagy Integer_Type_SuffixL
vagyl
jelenik meg tokenként közvetlenül egy egyesítetlen mínusz operátor token után (12.9.3. §), az eredmény (mindkét tokenből) a típusállandólong
, amelynek értéke−9223372036854775808
(−2⁶³). Minden más helyzetben az ilyen Integer_Literal típusú.ulong
Példa:
123 // decimal, int 10_543_765Lu // decimal, ulong 1_2__3___4____5 // decimal, int _123 // not a numeric literal; identifier due to leading _ 123_ // invalid; no trailing _allowed 0xFf // hex, int 0X1b_a0_44_fEL // hex, long 0x1ade_3FE1_29AaUL // hex, ulong 0x_abc // hex, int _0x123 // not a numeric literal; identifier due to leading _ 0xabc_ // invalid; no trailing _ allowed 0b101 // binary, int 0B1001_1010u // binary, uint 0b1111_1111_0000UL // binary, ulong 0B__111 // binary, int __0B111 // not a numeric literal; identifier due to leading _ 0B111__ // invalid; no trailing _ allowed
záró példa
6.4.5.4 Valós literálok
A valós literálokat a float
, double
és decimal
típusú értékek írására használják.
Real_Literal
: Decimal_Digit Decorated_Decimal_Digit* '.'
Decimal_Digit Decorated_Decimal_Digit* Exponent_Part? Real_Type_Suffix?
| '.' Decimal_Digit Decorated_Decimal_Digit* Exponent_Part? Real_Type_Suffix?
| Decimal_Digit Decorated_Decimal_Digit* Exponent_Part Real_Type_Suffix?
| Decimal_Digit Decorated_Decimal_Digit* Real_Type_Suffix
;
fragment Exponent_Part
: ('e' | 'E') Sign? Decimal_Digit Decorated_Decimal_Digit*
;
fragment Sign
: '+' | '-'
;
fragment Real_Type_Suffix
: 'F' | 'f' | 'D' | 'd' | 'M' | 'm'
;
Ha a Real_Type_Suffix nincs megadva, akkor a Real_Literal típusa double
. Ellenkező esetben a Real_Type_Suffix az alábbiak szerint határozza meg a valódi literál típusát:
- Az
F
vagyf
utótaggal rendelkező valós literál típusafloat
.Példa: A literálok
1f
,1.5f
és1e10f
123.456F
mind típusfloat
. záró példa - A valódi literál, amely
D
vagyd
utótaggal van ellátva,double
típusú.Példa: A literálok
1d
,1.5d
és1e10d
123.456D
mind típusdouble
. záró példa - Az
M
vagym
utótaggal ellátott valódi literál adecimal
típusú.Példa: A literálok
1m
,1.5m
és1e10m
123.456M
mind típusdecimal
. záró példa Ezt a literálist a rendszer a pontos érték megadásával konvertálja értékkédecimal
, és szükség esetén a legközelebbi ábrázolható értékre kerekít a banker kerekítésével (8.3.8. §). A literálban látható skálák megmaradnak, kivéve, ha az érték kerekítve van. Megjegyzés: Ezért a literál2.900m
úgy lesz elemezve, hogy a jeletdecimal
, az együtthatót0
és a2900
skálázást3
alkotja. végjegyzet
Ha a megadott literál mérete túl nagy ahhoz, hogy a megadott típusban szerepeljen, fordítási időhiba lép fel.
Megjegyzés: Egy Real_Literal soha nem hoz létre lebegőpontos végtelent. A nem nulla Real_Literal azonban nullára kerekíthető. végjegyzet
Egy float
vagy double
típusú valódi literál értékét az IEC 60559 "kerekítés legközelebbihez" módja határozza meg, ahol a döntetlen helyzeteket a "páros" irányába törjük (azaz az utolsó jelentős bit nullára van állítva), és minden számjegy jelentősnek számít.
Megjegyzés: Valós literálban a tizedespont után mindig szükségesek a tizedesjegyek. Például a
1.3F
egy valódi literál, de a1.F
nem. végjegyzetPélda:
1.234_567 // double .3e5f // float 2_345E-2_0 // double 15D // double 19.73M // decimal 1.F // parsed as a member access of F due to non-digit after . 1_.2F // invalid; no trailing _ allowed in integer part 1._234 // parsed as a member access of _234 due to non-digit after . 1.234_ // invalid; no trailing _ allowed in fraction .3e_5F // invalid; no leading _ allowed in exponent .3e5_F // invalid; no trailing _ allowed in exponent
záró példa
6.4.5.5 Karakterkonstansok
A karakterkonstans egyetlen karaktert jelöl, és idézőjelekben szereplő karakterből áll, ahogyan a .'a'
Character_Literal
: '\'' Character '\''
;
fragment Character
: Single_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
;
fragment Single_Character
// anything but ', \, and New_Line_Character
: ~['\\\u000D\u000A\u0085\u2028\u2029]
;
fragment Simple_Escape_Sequence
: '\\\'' | '\\"' | '\\\\' | '\\0' | '\\a' | '\\b' |
'\\f' | '\\n' | '\\r' | '\\t' | '\\v'
;
fragment Hexadecimal_Escape_Sequence
: '\\x' Hex_Digit Hex_Digit? Hex_Digit? Hex_Digit?
;
Megjegyzés: Olyan karakter, ami egy "backslash" karaktert (
\
) követ a karakterláncban, a következő karakterek egyikének kell lennie:'
,"
,\
,0
,a
,b
,f
,n
,r
,t
,u
,U
,x
,v
. Ellenkező esetben fordítási időhiba lép fel. végjegyzet
Megjegyzés: A
\x
Hexadecimal_Escape_Sequence használata hibalehetőséget jelenthet, és nehezen olvasható, mivel a hexadecimális számjegyek száma változó a következő\x
. Például a kódban:string good = "\x9Good text"; string bad = "\x9Bad text";
elsőre úgy tűnhet, hogy a kezdő karakter ugyanaz (
U+0009
egy tabulátor karakter) mindkét sztringben. Valójában a második karakterlánc aU+9BAD
kezdődik, mivel a "Bad" szó mindhárom betűje érvényes hexadecimális számjegy. Stílusbeli szempontból ajánlott elkerülni a\x
használatát, és ehelyett vagy konkrét escape szekvenciákat (\t
ebben a példában), vagy a fix hosszúságú\u
escape szekvenciát alkalmazni.végjegyzet
A hexadecimális feloldósorozat egyetlen Unicode UTF-16 kódegységet jelöl, amelynek értékét a "\x
" után a hexadecimális szám alkotja.
Ha a karakterkonstans által képviselt érték nagyobb U+FFFF
, fordítási időben hiba lép fel.
Egy karakterkonstans Unicode menekülési szekvenciájának (§6.4.2) az U+0000
és U+FFFF
közötti tartományba kell esnie.
Az egyszerű feloldójelek unicode karaktert jelölnek az alábbi táblázatban leírtak szerint.
Menekülési sorozat | Karakternév | Unicode-kódpont |
---|---|---|
\' |
Egy idézőjel | U+0027 |
\" |
Dupla idézőjel | U+0022 |
\\ |
Fordított perjel | U+005C |
\0 |
Null | U+0000 |
\a |
Riasztás | U+0007 |
\b |
Backspace | U+0008 |
\f |
Űrlapcsatorna | U+000C |
\n |
Új sor | U+000A |
\r |
Kocsi visszatérítés | U+000D |
\t |
Vízszintes lap | U+0009 |
\v |
Függőleges lap | U+000B |
A Character_Literal típusa a következőchar
: .
6.4.5.6 Sztringkonstansok
A C# a sztringkonstansok két formáját támogatja: normál sztringkonstansokat és szó szerinti sztringkonstansokat. A szokásos karakterlánc literál nulla vagy több karakterből áll, amelyek dupla idézőjelek közé vannak foglalva, és magában foglalhatja az egyszerű escape szekvenciákat (például a \t
a tabulátorkarakter esetében), valamint a hexadecimális és a Unicode escape szekvenciákat is.
A szó szerinti sztring literál egy @
karakterből áll, amelyet egy dupla idézőjel, egy nulla vagy több karakter és egy záró dupla idézőjel karakter követ.
Példa: Egy egyszerű példa.
@"hello"
záró példa
Szó szerinti formában, a határolójelek közötti karaktereket a rendszer változtatás nélkül értelmezi, az egyetlen kivétel egy Quote_Escape_Sequence, amely egy dupla idézőjelet jelöl. Az egyszerű feloldósorozatok, valamint a hexadecimális és Unicode feloldósorozatok nem kerülnek feldolgozásra a szó szerinti karaktersorozatokban. A szó szerinti sztringkonstans több sorra is kiterjedhet.
String_Literal
: Regular_String_Literal
| Verbatim_String_Literal
;
fragment Regular_String_Literal
: '"' Regular_String_Literal_Character* '"'
;
fragment Regular_String_Literal_Character
: Single_Regular_String_Literal_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
;
fragment Single_Regular_String_Literal_Character
// anything but ", \, and New_Line_Character
: ~["\\\u000D\u000A\u0085\u2028\u2029]
;
fragment Verbatim_String_Literal
: '@"' Verbatim_String_Literal_Character* '"'
;
fragment Verbatim_String_Literal_Character
: Single_Verbatim_String_Literal_Character
| Quote_Escape_Sequence
;
fragment Single_Verbatim_String_Literal_Character
: ~["] // anything but quotation mark (U+0022)
;
fragment Quote_Escape_Sequence
: '""'
;
Példa: A példa
string a = "Happy birthday, Joel"; // Happy birthday, Joel string b = @"Happy birthday, Joel"; // Happy birthday, Joel string c = "hello \t world"; // hello world string d = @"hello \t world"; // hello \t world string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt string h = @"\\server\share\file.txt"; // \\server\share\file.txt string i = "one\r\ntwo\r\nthree"; string j = @"one two three";
számos karakterlánc literált jelenít meg. Az utolsó sztringkonstans
j
egy többsoros, szó szerinti sztringkonstans. Az idézőjelek közötti karakterek, beleértve a fehér szóközt, például az új vonalkaraktereket, szó szerint megmaradnak, és minden páros idézőjelet egy ilyen karakter vált fel.záró példa
Megjegyzés: A szószerinti karakterláncok bármilyen sortörése az eredmény része. Ha a vonaltörések létrehozásához használt pontos karakterek szemantikailag relevánsak egy alkalmazás számára, minden olyan eszköz, amely a forráskód sortöréseit különböző formátumokra fordítja (például a "
\n
" és a "\r\n
" között) megváltoztatja az alkalmazás viselkedését. Ilyen helyzetekben a fejlesztőknek óvatosnak kell lenniük. végjegyzet
Megjegyzés: Mivel a hexadecimális feloldósorozatok változó számú hexa számjegyet tartalmazhatnak, a sztringkonstans
"\x123"
egyetlen, hexa értékkel123
rendelkező karaktert tartalmaz. Ha olyan karakterláncot szeretne létrehozni, amely tartalmazza a hexadecimális értékű12
karaktert, amit a3
követ, írhat"\x00123"
vagy"\x12"
+"3"
helyette. végjegyzet
A String_Literal típusa string
.
Minden sztringkonstans nem feltétlenül eredményez új sztringpéldányt. Ha két vagy több sztringkonstans, amely a sztringegyenlőség operátorának (12.12.8. §) szerint egyenértékű, ugyanabban a szerelvényben jelenik meg, ezek a sztringkonstansok ugyanarra a sztringpéldányra vonatkoznak.
Például: Például a kimenet által előállított
class Test { static void Main() { object a = "hello"; object b = "hello"; System.Console.WriteLine(a == b); } }
azért van,
True
mert a két literál ugyanarra a karakterlánc példányra hivatkozik.záró példa
6.4.5.7 A null literál
null_literal
: NULL
;
Megjegyzés: A null_literal egy elemző szabály, mivel nem vezet be új azonosítótípust. végjegyzet
A null_literal egy null
értéket jelölnek. Nem rendelkezik típussal, de bármely hivatkozástípusra vagy null értékű értéktípusra konvertálható null literális átalakítással (10.2.7. §).
6.4.6 Operátorok és írásjelek
Többféle operátor és írásjel is létezik. Az operátorok a kifejezésekben egy vagy több operandust érintő műveletek leírására szolgálnak.
Példa: A kifejezés
a + b
az+
operátorral adja hozzá a két operandusta
ésb
a . záró példa
Az írásjelek csoportosításra és elkülönítésre szolgálnak.
operator_or_punctuator
: '{' | '}' | '[' | ']' | '(' | ')' | '.' | ',' | ':' | ';'
| '+' | '-' | ASTERISK | SLASH | '%' | '&' | '|' | '^' | '!' | '~'
| '=' | '<' | '>' | '?' | '??' | '::' | '++' | '--' | '&&' | '||'
| '->' | '==' | '!=' | '<=' | '>=' | '+=' | '-=' | '*=' | '/=' | '%='
| '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' | '??='
;
right_shift
: '>' '>'
;
right_shift_assignment
: '>' '>='
;
Megjegyzés: right_shift és right_shift_assignment elemző szabályok, mivel nem vezetnek be új token típust, hanem két token sorozatát jelölik. A operator_or_punctuator szabály csak leíró célokra létezik, és a nyelvhelyesség más részén nem használatos. végjegyzet
A right_shift két >
és >
tokenből áll. Hasonlóképpen, right_shift_assignment a két tokenekből >
és >=
. A szintaktikai nyelvhelyesség többi produkcióitól eltérően ezen produkciók két tokenje között semmilyen karakter (még a szóköz sem) nem engedélyezett. Ezeket a termeléseket kifejezetten a type_parameter_lists megfelelő kezelése érdekében kezelik (15.2.3. §).
Megjegyzés: A generikusok C#
>>
-hoz való hozzáadása előtt,>>=
és>>
mindkettő egyetlen token volt. Azonban az általános szintaxis a<
és>
karaktereket a típusparaméterek és típusargumentumok elválasztására használja. Gyakran kívánatos olyan beágyazott összetett típusok használata, mint példáulList<Dictionary<string, int>>
. Ahelyett, hogy a programozónak szóközzel kellett volna elválasztania a>
-t és>
-t, megváltoztatták a két operator_or_punctuator definícióját. végjegyzet
6.5 Előfeldolgozási irányelvek
6.5.1 Általános
Az előfeldolgozási irányelvek lehetővé teszik a fordítási egységek szakaszainak feltételes kihagyását, a hiba- és figyelmeztetési feltételek jelentését, a forráskód különböző régióinak kijelölését és a null értékű környezet beállítását.
Megjegyzés: Az "előfeldolgozási irányelvek" kifejezés csak a C és C++ programozási nyelvekkel való konzisztenciára szolgál. A C#-ban nincs külön előfeldolgozási lépés; az előfeldolgozási irányelveket a lexikális elemzési fázis részeként dolgozzák fel. végjegyzet
PP_Directive
: PP_Start PP_Kind PP_New_Line
;
fragment PP_Kind
: PP_Declaration
| PP_Conditional
| PP_Line
| PP_Diagnostic
| PP_Region
| PP_Pragma
| PP_Nullable
;
// Only recognised at the beginning of a line
fragment PP_Start
// See note below.
: { getCharPositionInLine() == 0 }? PP_Whitespace? '#' PP_Whitespace?
;
fragment PP_Whitespace
: ( [\p{Zs}] // any character with Unicode class Zs
| '\u0009' // horizontal tab
| '\u000B' // vertical tab
| '\u000C' // form feed
)+
;
fragment PP_New_Line
: PP_Whitespace? Single_Line_Comment? New_Line
;
Megjegyzés:
- Az előfeldolgozási nyelvhelyesség egyetlen lexikális jogkivonatot
PP_Directive
határoz meg, amelyet az összes előfeldolgozási irányelvhez használnak. Az egyes előfeldolgozási irányelvek szemantikája ebben a nyelvi specifikációban van meghatározva, de nem az implementálásuk módjában.- A
PP_Start
töredék csak egy sor elején ismerhető fel, agetCharPositionInLine() == 0
fenti ANTLR lexikális predikátum egy olyan módszert javasol, amelyben ez megvalósítható, és csak informatív, a megvalósítás más stratégiát használhat.végjegyzet
A következő előfeldolgozási irányelvek érhetők el:
-
#define
és#undef
, amelyek a feltételes fordítási szimbólumok definiálására és nem definiálására szolgálnak (6.5.4. §). -
#if
,#elif
,#else
és#endif
, amelyek a forráskód feltételes szakaszainak kihagyására szolgálnak (6.5.5.5. §). -
#line
, amely a hibák és figyelmeztetések során kibocsátott vonalszámok szabályozására szolgál (6.5.8. §). -
#error
, amely hibákat jelez (§6.5.6.). -
#region
és#endregion
, amelyek a forráskód szakaszainak explicit megjelölésére szolgálnak (6.5.7. §). -
#nullable
, amely a null értékű környezet megadására szolgál (6.5.9. §). -
#pragma
, amely a fordító számára választható környezeti információk megadására szolgál (6.5.10. §).
Az előfeldolgozási irányelv mindig külön forráskódsort foglal el, és mindig egy karakterrel és egy #
előfeldolgozási irányelv nevével kezdődik. Szóköz is előfordulhat a #
karakter előtt, valamint a #
karakter és az irányelv neve között.
Egy #define
, #undef
, #if
, #elif
, #else
, #endif
, #line
, vagy #endregion
direktívát tartalmazó #nullable
forrásvonal egysoros megjegyzéssel végződhet. A tagolt megjegyzések (a /* */
megjegyzések stílusa) nem engedélyezettek az előfeldolgozási irányelveket tartalmazó forrásvonalakon.
Az előfeldolgozási irányelvek nem részei a C#szintaktikai nyelvtanának. Az előfeldolgozási irányelvek azonban felhasználhatók a tokenek sorozatainak beillesztésére vagy kizárására, és így befolyásolhatják a C# program jelentését.
Példa: Fordításkor a program
#define A #undef B class C { #if A void F() {} #else void G() {} #endif #if B void H() {} #else void I() {} #endif }
pontosan ugyanazt az elem-sorozatot eredményezi, mint a program
class C { void F() {} void I() {} }
Így, míg lexikálisan a két program meglehetősen eltérő, szintaktikailag azonosak.
záró példa
6.5.2 Feltételes fordítási szimbólumok
A #if
, #elif
, #else
és #endif
irányelvek által biztosított feltételes fordítási funkciókat az előfeldolgozási kifejezések (§6.5.3) és a feltételes fordítási szimbólumok vezérlik.
fragment PP_Conditional_Symbol
// Must not be equal to tokens TRUE or FALSE. See note below.
: Basic_Identifier
;
Figyelje meg, hogy az implementáció hogyan kényszeríti ki az engedélyezett Basic_Identifier értékekre vonatkozó korlátozást, az implementálási probléma. végjegyzet
Két feltételes fordítási szimbólum akkor tekinthető azonosnak, ha az alábbi átalakítások alkalmazása után azonosak, sorrendben:
- Minden Unicode_Escape_Sequence a megfelelő Unicode-karakterévé alakul át.
- Minden Formatting_Characters el lesz távolítva.
A feltételes fordítási szimbólumnak két lehetséges állapota van: definiált vagy nem definiált. A fordítási egység lexikális feldolgozásának elején a feltételes fordítási szimbólum nincs meghatározva, kivéve, ha azt külső mechanizmus (például parancssori fordítóbeállítás) explicit módon definiálta. Az #define
irányelv feldolgozásakor az irányelvben elnevezett feltételes fordítási szimbólum az adott összeállítási egységben lesz definiálva. A szimbólum mindaddig definiálva marad, amíg #undef
az adott szimbólumra vonatkozó irányelv feldolgozásra nem kerül, vagy amíg el nem éri a fordítási egység végét. Ennek az a következménye, hogy #define
#undef
az egyik fordítási egységben lévő irányelveknek nincs hatása az ugyanabban a programban lévő más fordítási egységekre.
Ha egy előfeldolgozási kifejezésben (6.5.3. §) hivatkoznak rá, a megadott feltételes fordítási szimbólum logikai értékkel true
rendelkezik, a nem definiált feltételes fordítási szimbólum pedig logikai értékkel false
. Nincs szükség arra, hogy a feltételes fordítási szimbólumok explicit módon deklarálva legyenek, mielőtt előfeldolgozási kifejezésekben hivatkoznának rájuk. Ehelyett a be nem jelentett szimbólumok egyszerűen nem definiáltak, és így rendelkeznek az értékkel false
.
A feltételes fordítási szimbólumok névtere eltér a C#-program összes többi elnevezett entitásától. A feltételes fordítási szimbólumokra csak irányelvekben #define
és #undef
előfeldolgozási kifejezésekben lehet hivatkozni.
6.5.3 Előfeldolgozási kifejezések
Előfeldolgozó kifejezések előfordulhatnak a #if
és #elif
irányelvekben. Az operátorok !
(csak a logikai eltagolás előtagja), ==
, !=
, &&
és ||
az előfeldolgozási kifejezésekben engedélyezettek, és zárójelek használhatók csoportosításhoz.
fragment PP_Expression
: PP_Whitespace? PP_Or_Expression PP_Whitespace?
;
fragment PP_Or_Expression
: PP_And_Expression (PP_Whitespace? '||' PP_Whitespace? PP_And_Expression)*
;
fragment PP_And_Expression
: PP_Equality_Expression (PP_Whitespace? '&&' PP_Whitespace?
PP_Equality_Expression)*
;
fragment PP_Equality_Expression
: PP_Unary_Expression (PP_Whitespace? ('==' | '!=') PP_Whitespace?
PP_Unary_Expression)*
;
fragment PP_Unary_Expression
: PP_Primary_Expression
| '!' PP_Whitespace? PP_Unary_Expression
;
fragment PP_Primary_Expression
: TRUE
| FALSE
| PP_Conditional_Symbol
| '(' PP_Whitespace? PP_Expression PP_Whitespace? ')'
;
Ha egy előfeldolgozási kifejezésben hivatkoznak rá, a megadott feltételes fordítási szimbólum logikai értékkel true
rendelkezik, a nem definiált feltételes fordítási szimbólum pedig logikai értékkel false
.
Az előfeldolgozási kifejezések kiértékelése mindig logikai értéket ad. Az előfeldolgozási kifejezések kiértékelési szabályai megegyeznek az állandó kifejezések (§12.23) szabályaival, azzal a kivétellel, hogy az egyetlen felhasználó által definiált entitások, amelyekre hivatkozni lehet, feltételes fordítási szimbólumok.
6.5.4 Definíciós irányelvek
A definíciós irányelvek a feltételes fordítási szimbólumok meghatározására vagy nem definiálására szolgálnak.
fragment PP_Declaration
: 'define' PP_Whitespace PP_Conditional_Symbol
| 'undef' PP_Whitespace PP_Conditional_Symbol
;
Az #define
irányelv feldolgozása eredményezi a megadott feltételes szimbólum definiálását, az irányelv utáni kódsortól kezdve. Hasonlóképpen, az #undef
irányelv feldolgozása miatt a megadott feltételes fordítási szimbólum definiálatlanná válik, kezdve az irányelvet követő forrásvonallal.
A bármely #define
és #undef
irányelvnek a fordítási egység első tokenje előtt kell szerepelnie a fordítási egységben (6.4. §), különben fordítási idejű hiba lép fel. Bélérzet szerint, a #define
és #undef
irányelveknek meg kell előzniük a "valódi kódot" a fordítási egységben.
Példa: A példa:
#define Enterprise #if Professional || Enterprise #define Advanced #endif namespace Megacorp.Data { #if Advanced class PivotTable {...} #endif }
azért érvényes, mert az
#define
irányelvek megelőzik az első tokent (anamespace
kulcsszót) a fordítási egységben.záró példa
Példa: Az alábbi példa fordítási idejű hibát eredményez, mert egy #define utasítás egy valós kódrészt követ.
#define A namespace N { #define B #if B class Class1 {} #endif }
záró példa
A #define
meghatározhat egy már definiált feltételes fordítási szimbólumot anélkül, hogy bármi közbeavatkozna #undef
ahhoz a szimbólumhoz.
Példa: Az alábbi példa egy A feltételes fordítási szimbólumot határoz meg, majd újra definiálja.
#define A #define A
Az olyan fordítók esetében, amelyek lehetővé teszik a feltételes fordítási szimbólumok fordítási lehetőségként való definiálását, az ilyen újradefiniálás másik módja a szimbólum fordítási lehetőségként és a forrásban való definiálása.
záró példa
A #undef
"definiálás megszüntetése" egy nem definiált feltételes fordítási szimbólum esetén.
Példa: Az alábbi példa egy feltételes fordítási szimbólumot
A
határoz meg, majd kétszer visszavonja; bár a másodiknak#undef
nincs hatása, továbbra is érvényes.#define A #undef A #undef A
záró példa
6.5.5 Feltételes fordítási irányelvek
A feltételes fordítási irányelvek a fordítási egység részeinek feltételes belefoglalására vagy kizárására szolgálnak.
fragment PP_Conditional
: PP_If_Section
| PP_Elif_Section
| PP_Else_Section
| PP_Endif
;
fragment PP_If_Section
: 'if' PP_Whitespace PP_Expression
;
fragment PP_Elif_Section
: 'elif' PP_Whitespace PP_Expression
;
fragment PP_Else_Section
: 'else'
;
fragment PP_Endif
: 'endif'
;
A feltételes fordítási irányelveket olyan csoportokba kell írni, amelyek sorrendben egy #if
irányelvből, egy vagy több #elif
irányelvből, nulla vagy egy #else
irányelvből és egy #endif
irányelvből állnak. Az irányelvek között a forráskód feltételes szakaszai találhatók. Minden szakaszt közvetlenül az előző irányelv szabályoz. A feltételes szakaszok maguk is tartalmazhatnak beágyazott feltételes fordítási irányelveket, feltéve, hogy ezek az irányelvek teljes csoportokat alkotnak.
A rendszer legfeljebb az egyik tartalmazott feltételes szakaszt választja ki a normál lexikális feldolgozáshoz:
- A PP_Expression-ek és a
#if
meg#elif
irányelvek kiértékelése sorrendben történik, amíg az egyik értékeléstrue
eredményt ad. Ha egy kifejezés hozamot eredményeztrue
, a megfelelő direktívát követő feltételes szakasz lesz kiválasztva. - Ha az összes PP_Expression értéket ad
false
, és ha jelen van egy#else
irányelv, akkor a#else
irányelvet követő feltételes szakasz kerül kiválasztásra. - Ellenkező esetben nincs feltételes szakasz kijelölve.
Ha van ilyen, a kiválasztott feltételes szakasz normál input_section-ként kerül feldolgozásra: a szakaszban található forráskódnak meg kell felelnie a lexikális nyelvtannak; tokenek jönnek létre a szakasz forráskódjából, és a szakasz előfeldolgozási irányelvei rendelkeznek az előírt hatásokkal.
A rendszer kihagyja a fennmaradó feltételes szakaszokat, és az előfeldolgozási irányelvek kivételével nem jön létre jogkivonat a forráskódból. Ezért az előfeldolgozási irányelvek kivételével kihagyott forráskód lexikálisan helytelen lehet. A kihagyott előfeldolgozási irányelveknek lexikálisan helyesnek kell lenniük, de más módon nem dolgozhatók fel. A kihagyott feltételes szakaszokon belül a beágyazott (beágyazott #if...#endif
szerkezetekben található) feltételes szakaszok is kimaradnak.
Megjegyzés: A fenti nyelvtan nem veszi figyelembe azt a lehetőséget, hogy az előfeldolgozási utasítások közötti feltételes szakaszok lexikális hibákat tartalmazhatnak. Ezért a nyelvhelyesség nem ANTLR-kész, mivel csak lexikálisan helyes bemenetet támogat. végjegyzet
Példa: Az alábbi példa bemutatja, hogyan beágyazhatók a feltételes fordítási irányelvek:
#define Debug // Debugging on #undef Trace // Tracing off class PurchaseTransaction { void Commit() { #if Debug CheckConsistency(); #if Trace WriteToLog(this.ToString()); #endif #endif CommitHelper(); } ... }
Az előfeldolgozási irányelvek kivételével a kihagyott forráskód nem vonatkozik lexikális elemzésre. Például a következő érvényes, a
#else
szakasz nem befejezett megjegyzése ellenére:#define Debug // Debugging on class PurchaseTransaction { void Commit() { #if Debug CheckConsistency(); #else /* Do something else #endif } ... }
Vegye figyelembe azonban, hogy az előfeldolgozási irányelveknek lexikálisan helyesnek kell lenniük még a forráskód kihagyott szakaszaiban is.
Az előfeldolgozási irányelvek nem lesznek feldolgozva, ha többsoros bemeneti elemekben jelennek meg. Például a program:
class Hello { static void Main() { System.Console.WriteLine(@"hello, #if Debug world #else Nebraska #endif "); } }
eredmények a kimenetben:
hello, #if Debug world #else Nebraska #endif
Sajátos esetekben a feldolgozott előfeldolgozási irányelvek halmaza a pp_expression értékelésétől függhet. Példa:
#if X /* #else /* */ class Q { } #endif
mindig ugyanazt a jogkivonat-adatfolyamot hozza létre (
class
Q
{
}
), függetlenül attól, hogy aX
definiálva van-e. HaX
meg van adva, az egyetlen feldolgozott irányelvek a#if
és a#endif
a többsoros megjegyzés miatt. HaX
nincs meghatározva, akkor három irányelv (#if
,#else
,#endif
) része az irányelvnek.záró példa
6.5.6 Diagnosztikai irányelvek
A diagnosztikai irányelvek kifejezetten olyan hibaüzenetek és figyelmeztető üzenetek létrehozására szolgálnak, amelyek ugyanúgy jelennek meg, mint más fordítási időpontokkal kapcsolatos hibák és figyelmeztetések.
fragment PP_Diagnostic
: 'error' PP_Message?
| 'warning' PP_Message?
;
fragment PP_Message
: PP_Whitespace Input_Character*
;
Példa: A példa
#if Debug && Retail #error A build can't be both debug and retail #endif class Test {...}
Fordítási időben hibaüzenetet ("Egy build nem lehet egyszerre hibakeresési és kiadási") kapunk, ha mind a
Debug
, mind aRetail
feltételes fordítási szimbólumok definiálva vannak. Vegye figyelembe, hogy egy PP_Message tetszőleges szöveget tartalmazhat, pontosabban nem kell jól formázott jogkivonatokat tartalmaznia, ahogyan azt a szócan't
egyetlen idézőjele is mutatja.záró példa
6.5.7 Régiókra vonatkozó irányelvek
A régió irányelvei kifejezetten a forráskód régióinak megjelölésére szolgálnak.
fragment PP_Region
: PP_Start_Region
| PP_End_Region
;
fragment PP_Start_Region
: 'region' PP_Message?
;
fragment PP_End_Region
: 'endregion' PP_Message?
;
Nincs szemantikai jelentés egy régióhoz csatolva; a régiókat a programozó vagy a forráskód egy szakaszának megjelölésére szolgáló automatizált eszközök használják. Minden #endregion
irányelvnek egy #region
irányelvnek kell megfelelnie. Az #region
vagy #endregion
irányelvben megadott üzenetnek ugyanúgy nincs szemantikai jelentése; csupán a régió azonosítására szolgál. Az egyezés #region
és #endregion
az irányelvek eltérő PP_Messagelehetnek.
Egy régió lexikális feldolgozása:
#region
...
#endregion
pontosan megfelel az űrlap feltételes fordítási irányelvének lexikális feldolgozásának:
#if true
...
#endif
Megjegyzés: Ez azt jelenti, hogy egy régió tartalmazhat egy vagy több
#if
/.../#endif
, vagy tartalmazhat egy feltételes szakaszt a#if
/.../#endif
;-on belül, de a régió nem fedheti át a#if
/.../#endif
-nak csak egy részét, vagy a kezdés és a befejezés különböző feltételes szakaszokban végződhet. végjegyzet
6.5.8 Vonalirányok
A sorirányelvek használhatók a fordító által jelentett sorszámok és fordítási egységek neveinek módosítására a kimenetben, mint például a figyelmeztetések és hibák esetén. Ezeket az értékeket a hívóinformációs attribútumok is használják (22.5.6. §).
Megjegyzés: A sorirányokat leggyakrabban olyan metaprogramozási eszközökben használják, amelyek más szövegbevitelből C#-forráskódot hoznak létre. végjegyzet
fragment PP_Line
: 'line' PP_Whitespace PP_Line_Indicator
;
fragment PP_Line_Indicator
: Decimal_Digit+ PP_Whitespace PP_Compilation_Unit_Name
| Decimal_Digit+
| DEFAULT
| 'hidden'
;
fragment PP_Compilation_Unit_Name
: '"' PP_Compilation_Unit_Name_Character* '"'
;
fragment PP_Compilation_Unit_Name_Character
// Any Input_Character except "
: ~('\u000D' | '\u000A' | '\u0085' | '\u2028' | '\u2029' | '"')
;
Ha nincsenek #line
irányelvek, a fordító valódi sorszámokat és fordítási egységek nevét jelenti a kimenetében. Olyan
A maximális megengedett Decimal_Digit+
érték a implementáció által definiált.
Egy #line default
irányelv visszavonja az összes korábbi #line
irányelv hatását. A fordító a következő sorok valós sorinformációkat ad, pontosan úgy, mintha nem dolgoztak volna fel #line
direktívákat.
Az #line hidden
irányelv nincs hatással a hibajelzésekben szereplő fordítási egységekre és a programsor számokra, sem arra, amit a CallerLineNumberAttribute
használatával (22.5.6.2. §) állítanak elő. A forrásszintű hibakeresési eszközöket úgy kívánja befolyásolni, hogy hibakereséskor az #line hidden
irányelv és az azt követő #line
irányelv közötti összes sor (azaz nem #line hidden
) ne rendelkezzen sorszámadatokkal, és a kódon keresztüli lépegetés során teljes egészében ki legyen hagyva.
Megjegyzés: Bár a PP_Compilation_Unit_Name tartalmazhatnak olyan szöveget, amely menekülési sorozatnak tűnik, az ilyen szöveg nem menekülési sorozat; ebben az összefüggésben a "
\
" karakter egyszerűen egy szokásos fordított perjel karaktert jelöl. végjegyzet
6.5.9 Érvénytelen irányelv
A null értékű irányelv szabályozza a null értékű környezetet az alábbiak szerint.
fragment PP_Nullable
: 'nullable' PP_Whitespace PP_Nullable_Action
(PP_Whitespace PP_Nullable_Target)?
;
fragment PP_Nullable_Action
: 'disable'
| 'enable'
| 'restore'
;
fragment PP_Nullable_Target
: 'warnings'
| 'annotations'
;
A null értékű irányelv beállítja a további kódsorok számára elérhető jelzőket, amíg egy másik null értékű irányelv felül nem bírálja azt, vagy amíg el nem éri a fordítási _unit végét. A null értékű környezet két jelölőt tartalmaz: széljegyzeteket és figyelmeztetéseket. A null értékű irányelv minden formájának hatása a következő:
-
#nullable disable
: Letiltja a null értékű széljegyzeteket és a null értékű figyelmeztetések jelzőit. -
#nullable enable
: Engedélyezi a null értékű széljegyzeteket és a null értékű figyelmeztetések jelzőit is. -
#nullable restore
: Visszaállítja a széljegyzeteket és a figyelmeztetési jelzőket a külső mechanizmus által megadott állapotra, ha vannak ilyenek. -
#nullable disable annotations
: Letiltja a null értékű széljegyzetek jelzőt. A "nullable warnings" jelző értéke változatlan marad. -
#nullable enable annotations
: Engedélyezi a nullálható annotációk kapcsolóját. A null értékű figyelmeztetések jelzője nincs hatással. -
#nullable restore annotations
: Visszaállítja a null értékű széljegyzetek jelzőt a külső mechanizmus által megadott állapotra, ha vannak ilyenek. A nullable figyelmeztetések jelzője nincs érintve. -
#nullable disable warnings
: Letiltja a nullable figyelmeztetések jelzőjét. A null értékű annotációk jelzője nincs hatással. -
#nullable enable warnings
: Engedélyezi a null értékű figyelmeztetések jelzőt. A null értékű annotációk jelölője nincs érintve. -
#nullable restore warnings
: Visszaállítja a null értékű figyelmeztetések jelzőt a külső mechanizmus által megadott állapotra, ha van ilyen. A null értékű annotációk jelölése változatlan marad.
A kifejezések null értékű állapotát mindig nyomon követi a rendszer. A széljegyzetjelző állapota és a null értékű széljegyzetek jelenléte vagy hiánya határozza meg a változódeklaráció ?
kezdeti null állapotát. A figyelmeztetések csak akkor jelennek meg, ha a figyelmeztetések jelzője engedélyezve van.
Példa: A példa
#nullable disable string x = null; string y = ""; #nullable enable Console.WriteLine(x.Length); // Warning Console.WriteLine(y.Length);
fordítási idő figyelmeztetést állít elő ("ahogy
x
vannull
"). A null értékű állapotx
mindenhol nyomon követhető. Figyelmeztetést ad ki, ha a figyelmeztetések jelzője engedélyezve van.záró példa
6.5.10 Pragma-irányelvek
Az #pragma
előfeldolgozási irányelv a fordítóprogram számára környezetfüggő információk megadására szolgál.
Megjegyzés: Egy fordító például olyan irányelveket adhat meg
#pragma
, amelyek
- Adott figyelmeztető üzenetek engedélyezése vagy letiltása a következő kód összeállításakor.
- Adja meg, hogy mely optimalizálásokat kell alkalmazni a következő kódra.
- Adja meg a hibakereső által használandó információkat.
végjegyzet
fragment PP_Pragma
: 'pragma' PP_Pragma_Text?
;
fragment PP_Pragma_Text
: PP_Whitespace Input_Character*
;
Az PP_Pragma_TextInput_Characterkaraktereit a fordító egy implementáció által meghatározott módon értelmezi. Az irányelvben #pragma
megadott információk nem módosíthatják a program szemantikáját. Az #pragma
irányelv csak az ezen nyelvi specifikáció hatókörén kívül eső fordítói viselkedést módosíthatja. Ha egy fordító nem tudja értelmezni a Input_Characterkaraktereket, a fordító figyelmeztetést adhat; azonban nem eredményezhet fordítási hibát.
Megjegyzés: PP_Pragma_Text tetszőleges szöveget tartalmazhat, pontosabban nem kell jól formázott jogkivonatokat tartalmaznia. végjegyzet
ECMA C# draft specification