語彙結構Lexical structure

程式Programs

C # 程式 _ 是由一或多個 _原始_ 程式檔所組成,其正式形式稱為 _ 編譯單位 (編譯單位) 。A C# program _ consists of one or more _source files_, known formally as _ compilation units (Compilation units). 原始程式檔是 Unicode 字元的排序次序。A source file is an ordered sequence of Unicode characters. 來源檔案通常與檔案系統中的檔案有一對一的對應關係,但不需要這項對應。Source files typically have a one-to-one correspondence with files in a file system, but this correspondence is not required. 若要獲得最大的可攜性,建議使用 UTF-8 編碼來編碼檔案系統中的檔案。For maximal portability, it is recommended that files in a file system be encoded with the UTF-8 encoding.

就概念而言,程式是使用三個步驟來編譯:Conceptually speaking, a program is compiled using three steps:

  1. 轉換:將檔案從特定的字元所有產品和編碼配置轉換成 Unicode 字元序列。Transformation, which converts a file from a particular character repertoire and encoding scheme into a sequence of Unicode characters.
  2. 詞法分析,會將 Unicode 輸入字元的資料流程轉譯成權杖資料流程。Lexical analysis, which translates a stream of Unicode input characters into a stream of tokens.
  3. 語法分析,會將權杖的資料流程轉譯成可執行程式碼。Syntactic analysis, which translates the stream of tokens into executable code.

文法Grammars

此規格會使用兩個文法來呈現 c # 程式設計語言的語法。This specification presents the syntax of the C# programming language using two grammars. *詞彙文法 _ (詞法文法) 定義 Unicode 字元如何合併以形成行結束字元、空白字元、批註、標記和前置處理指示詞。The *lexical grammar _ (Lexical grammar) defines how Unicode characters are combined to form line terminators, white space, comments, tokens, and pre-processing directives. _ 語法 文法* (語法文法) 定義如何合併詞彙文法所產生的標記,以構成 c # 程式。The _ syntactic grammar* (Syntactic grammar) defines how the tokens resulting from the lexical grammar are combined to form C# programs.

文法標記法Grammar notation

您可以使用 ANTLR 文法工具的標記法,以 Backus-Naur 形式呈現詞彙和語法文法。The lexical and syntactic grammars are presented in Backus-Naur form using the notation of the ANTLR grammar tool.

語彙文法Lexical grammar

C # 的詞法文法會以「 詞彙分析」、「 標記」和「 前置處理」指示詞呈現。The lexical grammar of C# is presented in Lexical analysis, Tokens, and Pre-processing directives. 「詞彙文法」的終端符號是 Unicode 字元集的字元,而「詞彙文法」會指定如何合併字元 , (token) 、空白字元 (空白字元) 、批註 (批註) 和前置處理指示詞 (前置處理 指示詞) 。The terminal symbols of the lexical grammar are the characters of the Unicode character set, and the lexical grammar specifies how characters are combined to form tokens (Tokens), white space (White space), comments (Comments), and pre-processing directives (Pre-processing directives).

C # 程式中的每個原始程式檔必須符合 (詞法分析) 的「詞彙文法」輸入 生產。Every source file in a C# program must conform to the input production of the lexical grammar (Lexical analysis).

語法文法Syntactic grammar

本章節後面的章節和附錄中會顯示 c # 的語法文法。The syntactic grammar of C# is presented in the chapters and appendices that follow this chapter. 語法文法的終端符號是由詞法文法所定義的標記,而語法文法則指定如何合併標記以形成 c # 程式。The terminal symbols of the syntactic grammar are the tokens defined by the lexical grammar, and the syntactic grammar specifies how tokens are combined to form C# programs.

C # 程式中的每個原始程式檔必須符合語法文法 (編譯單位compilation_unit 生產) 。Every source file in a C# program must conform to the compilation_unit production of the syntactic grammar (Compilation units).

語彙分析Lexical analysis

輸入 生產定義 c # 原始程式檔的詞法結構。The input production defines the lexical structure of a C# source file. C # 程式中的每個原始程式檔都必須符合此詞彙文法生產環境。Each source file in a C# program must conform to this lexical grammar production.

input
    : input_section?
    ;

input_section
    : input_section_part+
    ;

input_section_part
    : input_element* new_line
    | pp_directive
    ;

input_element
    : whitespace
    | comment
    | token
    ;

有五個基本元素組成 c # 原始程式檔的詞法結構:行結束字元 (行結束字元) 、空白字元 (空白字元) 、批註 (批註) 、 權杖 (token) 和 前置處理指示詞 (前置處理 指示詞) 。Five basic elements make up the lexical structure of a C# source file: Line terminators (Line terminators), white space (White space), comments (Comments), tokens (Tokens), and pre-processing directives (Pre-processing directives). 在這些基本專案中,只有標記在 c # 程式的語法文法中很重要, (語法文法) 。Of these basic elements, only tokens are significant in the syntactic grammar of a C# program (Syntactic grammar).

C # 原始程式檔的詞法處理包含將檔案縮減成一連串的 token,這些標記會成為語法分析的輸入。The lexical processing of a C# source file consists of reducing the file into a sequence of tokens which becomes the input to the syntactic analysis. 行結束字元、空白字元和批註可以用來分隔標記,而前置處理指示詞可能會略過原始程式檔的區段,否則這些詞法元素不會影響 c # 程式的語法結構。Line terminators, white space, and comments can serve to separate tokens, and pre-processing directives can cause sections of the source file to be skipped, but otherwise these lexical elements have no impact on the syntactic structure of a C# program.

如果插入字串常值 (插入 字串常 值) 單一 token 最初是由詞法分析所產生,但是會分成數個輸入元素,這些專案會重複受限於詞法分析,直到所有插入的字串常值都已解決為止。In the case of interpolated string literals (Interpolated string literals) a single token is initially produced by lexical analysis, but is broken up into several input elements which are repeatedly subjected to lexical analysis until all interpolated string literals have been resolved. 然後,產生的權杖會作為語法分析的輸入。The resulting tokens then serve as input to the syntactic analysis.

當數個詞法文法的生產符合原始程式檔中的一連串字元時,詞法處理一律會形成最長可能的詞彙元素。When several lexical grammar productions match a sequence of characters in a source file, the lexical processing always forms the longest possible lexical element. 例如,字元順序 // 會被當作單行批註的開頭處理,因為該詞法專案的長度超過單一 / token。For example, the character sequence // is processed as the beginning of a single-line comment because that lexical element is longer than a single / token.

行結束字元Line terminators

行結束字元會將 c # 原始程式檔的字元分成幾行。Line terminators divide the characters of a C# source file into lines.

new_line
    : '<Carriage return character (U+000D)>'
    | '<Line feed character (U+000A)>'
    | '<Carriage return character (U+000D) followed by line feed character (U+000A)>'
    | '<Next line character (U+0085)>'
    | '<Line separator character (U+2028)>'
    | '<Paragraph separator character (U+2029)>'
    ;

為了與可新增檔案結尾標記的原始程式碼編輯工具相容,並讓原始程式檔被視為正確終止的一連串列,會依序將下列轉換套用至 c # 程式中的每個原始程式檔:For compatibility with source code editing tools that add end-of-file markers, and to enable a source file to be viewed as a sequence of properly terminated lines, the following transformations are applied, in order, to every source file in a C# program:

  • 如果原始程式檔的最後一個字元是 () 的控制 Z 字元 U+001A ,則會刪除這個字元。If the last character of the source file is a Control-Z character (U+001A), this character is deleted.
  • 如果來源檔案不是空的,而且原始程式檔的 U+000D 最後一個字元不是換行字元 (U+000D) 、換行字元 (U+000A) 、行分隔符號 (U+2028) 或段落分隔符號 () ,則會將換行字元 () 新增至原始程式檔的結尾 U+2029A carriage-return character (U+000D) is added to the end of the source file if that source file is non-empty and if the last character of the source file is not a carriage return (U+000D), a line feed (U+000A), a line separator (U+2028), or a paragraph separator (U+2029).

註解Comments

支援兩種形式的註解:單行註解和分隔註解。Two forms of comments are supported: single-line comments and delimited comments.\ 單行批註 _ 以字元開頭 // ,並延伸至原始程式列的結尾。\ Single-line comments _ start with the characters // and extend to the end of the source line. *分隔的批註* 會以字元開頭 /_ ,並以字元結尾 */_Delimited comments_ start with the characters /_ and end with the characters */. 分隔註解可能會跨越多行。Delimited comments may span multiple lines.

comment
    : single_line_comment
    | delimited_comment
    ;

single_line_comment
    : '//' input_character*
    ;

input_character
    : '<Any Unicode character except a new_line_character>'
    ;

new_line_character
    : '<Carriage return character (U+000D)>'
    | '<Line feed character (U+000A)>'
    | '<Next line character (U+0085)>'
    | '<Line separator character (U+2028)>'
    | '<Paragraph separator character (U+2029)>'
    ;

delimited_comment
    : '/*' delimited_comment_section* asterisk+ '/'
    ;

delimited_comment_section
    : '/'
    | asterisk* not_slash_or_asterisk
    ;

asterisk
    : '*'
    ;

not_slash_or_asterisk
    : '<Any Unicode character except / or *>'
    ;

註解不會巢狀化。Comments do not nest. 字元序列 /**/ 批註中沒有特殊意義,而且 //// 分隔符號的 /* 批註內沒有特殊意義。The character sequences /* and */ have no special meaning within a // comment, and the character sequences // and /* have no special meaning within a delimited comment.

批註不會在字元和字串常值中處理。Comments are not processed within character and string literals.

範例The example

/* Hello, world program
   This program writes "hello, world" to the console
*/
class Hello
{
    static void Main() {
        System.Console.WriteLine("hello, world");
    }
}

包含分隔註解。includes a delimited comment.

範例The example

// 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");
    }
}

顯示數個單行註解。shows several single-line comments.

空白字元White space

空白字元會定義為具有 Unicode 類別 Zs 的任何字元 (其中包含空白字元) 以及水準定位字元、垂直定位字元和表單摘要字元。White space is defined as any character with Unicode class Zs (which includes the space character) as well as the horizontal tab character, the vertical tab character, and the form feed character.

whitespace
    : '<Any character with Unicode class Zs>'
    | '<Horizontal tab character (U+0009)>'
    | '<Vertical tab character (U+000B)>'
    | '<Form feed character (U+000C)>'
    ;

權杖Tokens

標記有數種類型:識別碼、關鍵字、常值、運算子和標點符號。There are several kinds of tokens: identifiers, keywords, literals, operators, and punctuators. 空白字元和批註不是標記,但會當做標記的分隔符號。White space and comments are not tokens, though they act as separators for tokens.

token
    : identifier
    | keyword
    | integer_literal
    | real_literal
    | character_literal
    | string_literal
    | interpolated_string_literal
    | operator_or_punctuator
    ;

Unicode 字元 escape 序列Unicode character escape sequences

Unicode 字元 escape 序列代表 Unicode 字元。A Unicode character escape sequence represents a Unicode character. Unicode 字元 escape 序列會在識別碼 (識別碼) 、字元常 值 () 的字元常 值,以及 (字串常 值) 的一般字串常值中處理。Unicode character escape sequences are processed in identifiers (Identifiers), character literals (Character literals), and regular string literals (String literals). 在任何其他位置都不會處理 Unicode 字元 escape (例如,用來形成 operator、標點符號或關鍵字) 。A Unicode character escape is not processed in any other location (for example, to form an operator, punctuator, or keyword).

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
    ;

Unicode escape 序清單示以 " \u " 或 "" 字元之後的十六進位數位所組成的單一 Unicode 字元 \UA Unicode escape sequence represents the single Unicode character formed by the hexadecimal number following the "\u" or "\U" characters. 由於 c # 使用字元和字串值的 Unicode 程式碼點的16位編碼方式,所以字元常值中不允許在 U + 10000 到 U + 10FFFF 範圍中的 Unicode 字元,而是使用字串常值中的 Unicode 代理組來表示。Since C# uses a 16-bit encoding of Unicode code points in characters and string values, a Unicode character in the range U+10000 to U+10FFFF is not permitted in a character literal and is represented using a Unicode surrogate pair in a string literal. 不支援0x10FFFF 以上程式碼點的 Unicode 字元。Unicode characters with code points above 0x10FFFF are not supported.

不會執行多個翻譯。Multiple translations are not performed. 例如,字串常 \u005Cu005C 值 "" 相當於 "" \u005C (而非 "") \For instance, the string literal "\u005Cu005C" is equivalent to "\u005C" rather than "\". Unicode 值 \u005C 是字元 " \ "。The Unicode value \u005C is the character "\".

範例The example

class Class1
{
    static void Test(bool \u0066) {
        char c = '\u0066';
        if (\u0066)
            System.Console.WriteLine(c.ToString());
    }        
}

顯示數個的用法 \u0066 ,也就是字母 "" 的 escape 序列 fshows several uses of \u0066, which is the escape sequence for the letter "f". 程式相當於The program is equivalent to

class Class1
{
    static void Test(bool f) {
        char c = 'f';
        if (f)
            System.Console.WriteLine(c.ToString());
    }        
}

識別碼Identifiers

本節所述的識別碼規則與 Unicode 標準附錄31所建議的規則完全一致,不同之處在于使用底線做為初始字元 (與 C 程式設計語言) 一樣,Unicode escape 序列可以在識別碼中使用,並 @ 允許 "" 字元做為前置詞,以啟用關鍵字作為識別碼。The rules for identifiers given in this section correspond exactly to those recommended by the Unicode Standard Annex 31, except that underscore is allowed as an initial character (as is traditional in the C programming language), Unicode escape sequences are permitted in identifiers, and the "@" character is allowed as a prefix to enable keywords to be used as identifiers.

identifier
    : available_identifier
    | '@' identifier_or_keyword
    ;

available_identifier
    : '<An identifier_or_keyword that is not a keyword>'
    ;

identifier_or_keyword
    : identifier_start_character identifier_part_character*
    ;

identifier_start_character
    : letter_character
    | '_'
    ;

identifier_part_character
    : letter_character
    | decimal_digit_character
    | connecting_character
    | combining_character
    | formatting_character
    ;

letter_character
    : '<A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl>'
    | '<A unicode_escape_sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl>'
    ;

combining_character
    : '<A Unicode character of classes Mn or Mc>'
    | '<A unicode_escape_sequence representing a character of classes Mn or Mc>'
    ;

decimal_digit_character
    : '<A Unicode character of the class Nd>'
    | '<A unicode_escape_sequence representing a character of the class Nd>'
    ;

connecting_character
    : '<A Unicode character of the class Pc>'
    | '<A unicode_escape_sequence representing a character of the class Pc>'
    ;

formatting_character
    : '<A Unicode character of the class Cf>'
    | '<A unicode_escape_sequence representing a character of the class Cf>'
    ;

如需上述 Unicode 字元類別的詳細資訊,請參閱 Unicode 標準3.0 版,第4.5 節。For information on the Unicode character classes mentioned above, see The Unicode Standard, Version 3.0, section 4.5.

有效識別碼的範例包括 " identifier1 "、" _identifier2 " 和 " @if "。Examples of valid identifiers include "identifier1", "_identifier2", and "@if".

符合規範的程式中的識別碼必須是 Unicode 正規化格式 C 所定義的標準格式,如 Unicode 標準附錄15所定義。An identifier in a conforming program must be in the canonical format defined by Unicode Normalization Form C, as defined by Unicode Standard Annex 15. 當遇到未採用正規化形式 C 的識別碼時,其行為為實作為定義;但是,不需要進行診斷。The behavior when encountering an identifier not in Normalization Form C is implementation-defined; however, a diagnostic is not required.

前置詞 " @ " 可將關鍵字當作識別碼使用,這在與其他程式設計語言互動時相當實用。The prefix "@" enables the use of keywords as identifiers, which is useful when interfacing with other programming languages. 字元 @ 不是識別碼的一部分,因此可能會在其他語言中將識別碼視為一般識別碼,但不含前置詞。The character @ is not actually part of the identifier, so the identifier might be seen in other languages as a normal identifier, without the prefix. 具有前置詞的識別碼 @ 稱為 逐字識別碼An identifier with an @ prefix is called a verbatim identifier. @允許對不是關鍵字的識別碼使用前置詞,但強烈建議您不要使用樣式。Use of the @ prefix for identifiers that are not keywords is permitted, but strongly discouraged as a matter of style.

範例:The example:

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);
    }
}

定義名為 "" 的類別, class 具有名為 "" 的靜態方法 static ,它會採用名為 "" 的參數 booldefines a class named "class" with a static method named "static" that takes a parameter named "bool". 請注意,因為關鍵字中不允許 Unicode 轉義,所以 token " cl\u0061ss " 是識別碼,而且與 "" 的識別碼相同 @classNote that since Unicode escapes are not permitted in keywords, the token "cl\u0061ss" is an identifier, and is the same identifier as "@class".

在套用下列轉換之後,如果兩個識別碼相同,則會視為相同:Two identifiers are considered the same if they are identical after the following transformations are applied, in order:

  • 移除前置詞 " @ " (如果使用的話)。The prefix "@", if used, is removed.
  • 每個 unicode_escape_sequence 都會轉換成其對應的 unicode 字元。Each unicode_escape_sequence is transformed into its corresponding Unicode character.
  • 移除任何 formatting_character s。Any formatting_character s are removed.

包含兩個連續底線字元的識別碼 (U+005F) 會保留供實作為使用。Identifiers containing two consecutive underscore characters (U+005F) are reserved for use by the implementation. 例如,執行可能會提供以兩個底線開頭的擴充關鍵字。For example, an implementation might provide extended keywords that begin with two underscores.

關鍵字Keywords

關鍵字 是保留的類似識別碼字元序列,不能當做識別碼使用,除非前面加上 @ 字元。A keyword is an identifier-like sequence of characters that is reserved, and cannot be used as an identifier except when prefaced by the @ character.

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'
    ;

在文法的某些地方,特定識別碼具有特殊意義,但不是關鍵字。In some places in the grammar, specific identifiers have special meaning, but are not keywords. 這類識別碼有時稱為「內容關鍵字」。Such identifiers are sometimes referred to as "contextual keywords". 例如,在屬性宣告中," get " 和 " set " 識別碼具有特殊意義 (存取 子) 。For example, within a property declaration, the "get" and "set" identifiers have special meaning (Accessors). get set 在這些位置中永遠不允許或以外的識別碼,因此這種使用方式不會與這些字組做為識別碼衝突。An identifier other than get or set is never permitted in these locations, so this use does not conflict with a use of these words as identifiers. 在其他情況下,例如在 var 隱含類型區域變數宣告中使用識別碼 "" (區域變數 宣告) ,內容關鍵字可能會與宣告的名稱衝突。In other cases, such as with the identifier "var" in implicitly typed local variable declarations (Local variable declarations), a contextual keyword can conflict with declared names. 在這種情況下,宣告的名稱優先于使用識別碼作為內容關鍵字。In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword.

常值Literals

「常值」是值的原始程式碼表示法。A literal is a source code representation of a value.

literal
    : boolean_literal
    | integer_literal
    | real_literal
    | character_literal
    | string_literal
    | null_literal
    ;

布林常值Boolean literals

有兩個布林常值: truefalseThere are two boolean literal values: true and false.

boolean_literal
    : 'true'
    | 'false'
    ;

Boolean_literal 的型別為 boolThe type of a boolean_literal is bool.

整數常值Integer literals

整數常值是用來寫入類型、、和的值 int uint long ulongInteger literals are used to write values of types int, uint, long, and ulong. 整數常值有兩種可能的格式: decimal 和十六進位。Integer literals have two possible forms: decimal and hexadecimal.

integer_literal
    : decimal_integer_literal
    | hexadecimal_integer_literal
    ;

decimal_integer_literal
    : decimal_digit+ integer_type_suffix?
    ;

decimal_digit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
    ;

integer_type_suffix
    : 'U' | 'u' | 'L' | 'l' | 'UL' | 'Ul' | 'uL' | 'ul' | 'LU' | 'Lu' | 'lU' | 'lu'
    ;

hexadecimal_integer_literal
    : '0x' hex_digit+ integer_type_suffix?
    | '0X' hex_digit+ integer_type_suffix?
    ;

hex_digit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
    | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f';

整數常值的類型取決於下列內容:The type of an integer literal is determined as follows:

  • 如果常值沒有後置詞,則它具有可表示其值的第一個類型: intuintlongulongIf the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
  • 如果常值是以 U 或為 u 結尾,則它具有可表示其值的第一個類型: uintulongIf the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong.
  • 如果常值是以 L 或為 l 結尾,則它具有可表示其值的第一個類型: longulongIf the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong.
  • 如果常值的尾碼為 ULUl 、、、、、 uL ul LU Lu lUlu ,則為類型 ulongIf the literal is suffixed by UL, Ul, uL, ul, LU, Lu, lU, or lu, it is of type ulong.

如果整數常值所代表的值超出 ulong 類型範圍,就會發生編譯階段錯誤。If the value represented by an integer literal is outside the range of the ulong type, a compile-time error occurs.

無論使用何種樣式,在 L 撰寫類型的常值時,建議使用 "" 而非 "" l long ,因為很容易將字母 " l " 與數位 " 1 " 混淆。As a matter of style, it is suggested that "L" be used instead of "l" when writing literals of type long, since it is easy to confuse the letter "l" with the digit "1".

若要允許將最 intlong 值寫入為十進位整數常值,有下列兩個規則存在:To permit the smallest possible int and long values to be written as decimal integer literals, the following two rules exist:

  • 當值為 2147483648 (2 ^ 31 的 decimal_integer_literal) ,且沒有 integer_type_suffix 出現在一元減號運算子 () 一元減號運算子 ( 之後,結果會是類型的常數,其 int 值為-2147483648) -2 ^ 31。When a decimal_integer_literal with the value 2147483648 (2^31) and no integer_type_suffix appears as the token immediately following a unary minus operator token (Unary minus operator), the result is a constant of type int with the value -2147483648 (-2^31). 在所有其他情況下,這類 decimal_integer_literal 的類型為 uintIn all other situations, such a decimal_integer_literal is of type uint.
  • decimal_integer_literal 值為 9223372036854775808 (2 ^ 63) 且沒有 integer_type_suffixinteger_type_suffix L ,或出現在 l 一元減號運算子 () 一元減號運算子 ( 之後,結果會是類型的常數,其 long 值為-9223372036854775808) -2 ^ 63。When a decimal_integer_literal with the value 9223372036854775808 (2^63) and no integer_type_suffix or the integer_type_suffix L or l appears as the token immediately following a unary minus operator token (Unary minus operator), the result is a constant of type long with the value -9223372036854775808 (-2^63). 在所有其他情況下,這類 decimal_integer_literal 的類型為 ulongIn all other situations, such a decimal_integer_literal is of type ulong.

實際常值Real literals

實際常值是用來寫入類型 floatdouble 和的值 decimalReal literals are used to write values of types float, double, and decimal.

real_literal
    : decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix?
    | '.' decimal_digit+ exponent_part? real_type_suffix?
    | decimal_digit+ exponent_part real_type_suffix?
    | decimal_digit+ real_type_suffix
    ;

exponent_part
    : 'e' sign? decimal_digit+
    | 'E' sign? decimal_digit+
    ;

sign
    : '+'
    | '-'
    ;

real_type_suffix
    : 'F' | 'f' | 'D' | 'd' | 'M' | 'm'
    ;

如果未指定任何 real_type_suffix ,則實際常值的類型為 doubleIf no real_type_suffix is specified, the type of the real literal is double. 否則,實數型別尾碼會決定實際常值的型別,如下所示:Otherwise, the real type suffix determines the type of the real literal, as follows:

  • F或的實數常 f 值為類型 floatA real literal suffixed by F or f is of type float. 例如,常值 1f1.5f1e10f123.456F 都是類型 floatFor example, the literals 1f, 1.5f, 1e10f, and 123.456F are all of type float.
  • D或的實數常 d 值為類型 doubleA real literal suffixed by D or d is of type double. 例如,常值 1d1.5d1e10d123.456D 都是類型 doubleFor example, the literals 1d, 1.5d, 1e10d, and 123.456D are all of type double.
  • M或的實數常 m 值為類型 decimalA real literal suffixed by M or m is of type decimal. 例如,常值 1m1.5m1e10m123.456M 都是類型 decimalFor example, the literals 1m, 1.5m, 1e10m, and 123.456M are all of type decimal. 此常值會藉 decimal 由取得確切的值來轉換成值,並在必要時,使用銀行家的舍入 (decimal 類型) 來四捨五入至最接近的可表示值。This literal is converted to a decimal value by taking the exact value, and, if necessary, rounding to the nearest representable value using banker's rounding (The decimal type). 除非舍入值或值為零 (,否則會保留常值中的任何小數位數,在後者的情況下,正負號和小數位數為 0) 。Any scale apparent in the literal is preserved unless the value is rounded or the value is zero (in which latter case the sign and scale will be 0). 因此,將會剖析常值 2.900m 以形成具有正負號 0 、係數和小數位數的十進位 2900 3Hence, the literal 2.900m will be parsed to form the decimal with sign 0, coefficient 2900, and scale 3.

如果指定的常值無法在指定的型別中表示,就會發生編譯時期錯誤。If the specified literal cannot be represented in the indicated type, a compile-time error occurs.

或類型之實數常值的值 float double ,是使用 IEEE 「四捨五入到最接近的」模式來決定。The value of a real literal of type float or double is determined by using the IEEE "round to nearest" mode.

請注意,在實際常值中,小數點後一律需要小數數位。Note that in a real literal, decimal digits are always required after the decimal point. 例如, 1.3F 是實數常值,但卻 1.F 不是。For example, 1.3F is a real literal but 1.F is not.

字元常值Character literals

字元常值代表單一字元,而且通常是由引號中的字元所組成,如下所示 'a'A character literal represents a single character, and usually consists of a character in quotes, as in 'a'.

注意: ANTLR 文法標記法會造成下列混淆!Note: The ANTLR grammar notation makes the following confusing! 在 ANTLR 中,當您撰寫時, \' 代表單引號 'In ANTLR, when you write \' it stands for a single quote '. 當您撰寫時, \\ 它代表單一反斜線 \And when you write \\ it stands for a single backslash \. 因此,字元常值的第一個規則表示開頭為單引號、字元,然後是單引號。Therefore the first rule for a character literal means it starts with a single quote, then a character, then a single quote. 而有11個可能的簡單 escape 序列是、、、、、、、、、 \' \" \\ \0 \a \b \f \n \r \t\vAnd the eleven possible simple escape sequences are \', \", \\, \0, \a, \b, \f, \n, \r, \t, \v.

character_literal
    : '\'' character '\''
    ;

character
    : single_character
    | simple_escape_sequence
    | hexadecimal_escape_sequence
    | unicode_escape_sequence
    ;

single_character
    : '<Any character except \' (U+0027), \\ (U+005C), and new_line_character>'
    ;

simple_escape_sequence
    : '\\\'' | '\\"' | '\\\\' | '\\0' | '\\a' | '\\b' | '\\f' | '\\n' | '\\r' | '\\t' | '\\v'
    ;

hexadecimal_escape_sequence
    : '\\x' hex_digit hex_digit? hex_digit? hex_digit?;

在字元 () 的反斜線字元後面的字元 \ 必須是下列其中一個字元: '" 、、、 \ 0 a b f n r t u U x v 、、、、、、、、、。A character that follows a backslash character (\) in a character must be one of the following characters: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. 否則,會發生編譯時期錯誤。Otherwise, a compile-time error occurs.

十六進位的 escape 序清單示單一的 Unicode 字元,其中的值是由十六進位數位後面的 " \x " 所組成。A hexadecimal escape sequence represents a single Unicode character, with the value formed by the hexadecimal number following "\x".

如果字元常值所代表的值大於 U+FFFF ,就會發生編譯時期錯誤。If the value represented by a character literal is greater than U+FFFF, a compile-time error occurs.

Unicode 字元 escape 序列 (字元常值中) 的 unicode 字元 escape 序列必須在的範圍內 U+0000 U+FFFFA Unicode character escape sequence (Unicode character escape sequences) in a character literal must be in the range U+0000 to U+FFFF.

簡單的 escape 序列代表 Unicode 字元編碼,如下表所述。A simple escape sequence represents a Unicode character encoding, as described in the table below.

逸出序列Escape sequence 字元名稱Character name Unicode 編碼Unicode encoding
\' 單引號Single quote 0x0027
\" 雙引號Double quote 0x0022
\\ 反斜線Backslash 0x005C
\0 NullNull 0x0000
\a 警示Alert 0x0007
\b 退格鍵Backspace 0x0008
\f 換頁字元Form feed 0x000C
\n 新行New line 0x000A
\r 歸位字元Carriage return 0x000D
\t 水平 Tab 鍵Horizontal tab 0x0009
\v 垂直 Tab 鍵Vertical tab 0x000B

Character_literal 的型別為 charThe type of a character_literal is char.

字串常值String literals

C # 支援兩種形式的字串常值: *一般字串常 值 _ 和 _ *逐字字串常 值 * *。C# supports two forms of string literals: regular string literals _ and _verbatim string literals**.

一般字串常值包含以雙引號括住的零或多個字元(如中所示), "hello" 而且可能包含簡單的 escape 序列 (例如 \t 針對定位字元) 和十六進位和 Unicode escape 序列。A regular string literal consists of zero or more characters enclosed in double quotes, as in "hello", and may include both simple escape sequences (such as \t for the tab character), and hexadecimal and Unicode escape sequences.

逐字字串常值是由 @ 後面加上雙引號字元的字元、零或多個字元,以及右雙引號字元所組成。A verbatim string literal consists of an @ character followed by a double-quote character, zero or more characters, and a closing double-quote character. 簡單的範例為 @"hello"A simple example is @"hello". 在逐字字串常值中,會逐字解讀分隔符號之間的字元,唯一的例外是 quote_escape_sequenceIn a verbatim string literal, the characters between the delimiters are interpreted verbatim, the only exception being a quote_escape_sequence. 尤其是,簡單的 escape 序列和十六進位和 Unicode escape 序列都不會在逐字字串常值中處理。In particular, simple escape sequences, and hexadecimal and Unicode escape sequences are not processed in verbatim string literals. 逐字字串常值可能橫跨多行。A verbatim string literal may span multiple lines.

string_literal
    : regular_string_literal
    | verbatim_string_literal
    ;

regular_string_literal
    : '"' regular_string_literal_character* '"'
    ;

regular_string_literal_character
    : single_regular_string_literal_character
    | simple_escape_sequence
    | hexadecimal_escape_sequence
    | unicode_escape_sequence
    ;

single_regular_string_literal_character
    : '<Any character except " (U+0022), \\ (U+005C), and new_line_character>'
    ;

verbatim_string_literal
    : '@"' verbatim_string_literal_character* '"'
    ;

verbatim_string_literal_character
    : single_verbatim_string_literal_character
    | quote_escape_sequence
    ;

single_verbatim_string_literal_character
    : '<any character except ">'
    ;

quote_escape_sequence
    : '""'
    ;

在 regular_string_literal_character 中 () 的反斜線字元之後的字元 \ 必須是下列其中一個字元: '" 、、、、、、、、、、、 \ 0 a b f n r t u U xvA character that follows a backslash character (\) in a regular_string_literal_character must be one of the following characters: ', ", \, 0, a, b, f, n, r, t, u, U, x, v. 否則,會發生編譯時期錯誤。Otherwise, a compile-time error occurs.

範例The example

string a = "hello, world";                   // hello, world
string b = @"hello, world";                  // hello, world

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";

顯示各種不同的字串常值。shows a variety of string literals. 最後一個字串常 j 值是跨越多行的逐字字串常值。The last string literal, j, is a verbatim string literal that spans multiple lines. 引號之間的字元(包括空白字元,例如分行符號)會逐字保留。The characters between the quotation marks, including white space such as new line characters, are preserved verbatim.

由於十六進位的 escape 序列可以有不定數目的十六進位數位,因此字串常 "\x123" 值包含具有十六進位值123的單一字元。Since a hexadecimal escape sequence can have a variable number of hex digits, the string literal "\x123" contains a single character with hex value 123. 若要建立字串,其中包含具有十六進位值12的字元,後面接著字元3,則可以撰寫 "\x00123" 或代替字元 3 "\x12" + "3"To create a string containing the character with hex value 12 followed by the character 3, one could write "\x00123" or "\x12" + "3" instead.

String_literal 的型別為 stringThe type of a string_literal is string.

每個字串常值都不一定會產生新的字串實例。Each string literal does not necessarily result in a new string instance. 如果兩個或多個字串常值(根據字串相等運算子) (字串等號比較運算子) 出現在相同的程式中,則這些字串常值會參考相同的字串實例。When two or more string literals that are equivalent according to the string equality operator (String equality operators) appear in the same program, these string literals refer to the same string instance. 例如,所產生的輸出For instance, the output produced by

class Test
{
    static void Main() {
        object a = "hello";
        object b = "hello";
        System.Console.WriteLine(a == b);
    }
}

True這是因為這兩個常值會參考相同的字串實例。is True because the two literals refer to the same string instance.

插補字串常值Interpolated string literals

插入字串常值類似于字串常值,但包含以和分隔的孔,也就是 { } 可以發生運算式的情況。Interpolated string literals are similar to string literals, but contain holes delimited by { and }, wherein expressions can occur. 在執行時間,系統會評估運算式的目的是要在發生洞的地方,將其文字格式取代為字串。At runtime, the expressions are evaluated with the purpose of having their textual forms substituted into the string at the place where the hole occurs. 字串插補的語法和語義會在 (插入 字串) 中描述。The syntax and semantics of string interpolation are described in section (Interpolated strings).

和字串常值一樣,內插字串常值可以是 regular 或逐字。Like string literals, interpolated string literals can be either regular or verbatim. 插入的一般字串常值會以 $" 和分隔 " ,而且插入的逐字字串常值會以 $@" 和分隔 "Interpolated regular string literals are delimited by $" and ", and interpolated verbatim string literals are delimited by $@" and ".

就像其他常值一樣,插入字串常值的詞法分析一開始會產生單一權杖,如下所示的文法。Like other literals, lexical analysis of an interpolated string literal initially results in a single token, as per the grammar below. 不過,在語法分析之前,插入字串常值的單一 token 會分成多個標記,以用於封入洞的字串部分,而在漏洞中發生的輸入元素則會再次以詞法的方式分析。However, before syntactic analysis, the single token of an interpolated string literal is broken into several tokens for the parts of the string enclosing the holes, and the input elements occurring in the holes are lexically analysed again. 這可能會產生更多插入的字串常值,但如果以詞法正確,最後將會產生一系列的標記,以便處理語法分析。This may in turn produce more interpolated string literals to be processed, but, if lexically correct, will eventually lead to a sequence of tokens for syntactic analysis to process.

interpolated_string_literal
    : '$' interpolated_regular_string_literal
    | '$' interpolated_verbatim_string_literal
    ;

interpolated_regular_string_literal
    : interpolated_regular_string_whole
    | interpolated_regular_string_start  interpolated_regular_string_literal_body interpolated_regular_string_end
    ;

interpolated_regular_string_literal_body
    : regular_balanced_text
    | interpolated_regular_string_literal_body interpolated_regular_string_mid regular_balanced_text
    ;

interpolated_regular_string_whole
    : '"' interpolated_regular_string_character* '"'
    ;

interpolated_regular_string_start
    : '"' interpolated_regular_string_character* '{'
    ;

interpolated_regular_string_mid
    : interpolation_format? '}' interpolated_regular_string_characters_after_brace? '{'
    ;

interpolated_regular_string_end
    : interpolation_format? '}' interpolated_regular_string_characters_after_brace? '"'
    ;

interpolated_regular_string_characters_after_brace
    : interpolated_regular_string_character_no_brace
    | interpolated_regular_string_characters_after_brace interpolated_regular_string_character
    ;

interpolated_regular_string_character
    : single_interpolated_regular_string_character
    | simple_escape_sequence
    | hexadecimal_escape_sequence
    | unicode_escape_sequence
    | open_brace_escape_sequence
    | close_brace_escape_sequence
    ;

interpolated_regular_string_character_no_brace
    : '<Any interpolated_regular_string_character except close_brace_escape_sequence and any hexadecimal_escape_sequence or unicode_escape_sequence designating } (U+007D)>'
    ;

single_interpolated_regular_string_character
    : '<Any character except \" (U+0022), \\ (U+005C), { (U+007B), } (U+007D), and new_line_character>'
    ;

open_brace_escape_sequence
    : '{{'
    ;

close_brace_escape_sequence
    : '}}'
    ;
    
regular_balanced_text
    : regular_balanced_text_part+
    ;

regular_balanced_text_part
    : single_regular_balanced_text_character
    | delimited_comment
    | '@' identifier_or_keyword
    | string_literal
    | interpolated_string_literal
    | '(' regular_balanced_text ')'
    | '[' regular_balanced_text ']'
    | '{' regular_balanced_text '}'
    ;
    
single_regular_balanced_text_character
    : '<Any character except / (U+002F), @ (U+0040), \" (U+0022), $ (U+0024), ( (U+0028), ) (U+0029), [ (U+005B), ] (U+005D), { (U+007B), } (U+007D) and new_line_character>'
    | '</ (U+002F), if not directly followed by / (U+002F) or * (U+002A)>'
    ;
    
interpolation_format
    : ':' interpolation_format_character+
    ;
    
interpolation_format_character
    : '<Any character except \" (U+0022), : (U+003A), { (U+007B) and } (U+007D)>'
    ;
    
interpolated_verbatim_string_literal
    : interpolated_verbatim_string_whole
    | interpolated_verbatim_string_start interpolated_verbatim_string_literal_body interpolated_verbatim_string_end
    ;

interpolated_verbatim_string_literal_body
    : verbatim_balanced_text
    | interpolated_verbatim_string_literal_body interpolated_verbatim_string_mid verbatim_balanced_text
    ;
    
interpolated_verbatim_string_whole
    : '@"' interpolated_verbatim_string_character* '"'
    ;
    
interpolated_verbatim_string_start
    : '@"' interpolated_verbatim_string_character* '{'
    ;
    
interpolated_verbatim_string_mid
    : interpolation_format? '}' interpolated_verbatim_string_characters_after_brace? '{'
    ;
    
interpolated_verbatim_string_end
    : interpolation_format? '}' interpolated_verbatim_string_characters_after_brace? '"'
    ;
    
interpolated_verbatim_string_characters_after_brace
    : interpolated_verbatim_string_character_no_brace
    | interpolated_verbatim_string_characters_after_brace interpolated_verbatim_string_character
    ;
    
interpolated_verbatim_string_character
    : single_interpolated_verbatim_string_character
    | quote_escape_sequence
    | open_brace_escape_sequence
    | close_brace_escape_sequence
    ;
    
interpolated_verbatim_string_character_no_brace
    : '<Any interpolated_verbatim_string_character except close_brace_escape_sequence>'
    ;
    
single_interpolated_verbatim_string_character
    : '<Any character except \" (U+0022), { (U+007B) and } (U+007D)>'
    ;
    
verbatim_balanced_text
    : verbatim_balanced_text_part+
    ;

verbatim_balanced_text_part
    : single_verbatim_balanced_text_character
    | comment
    | '@' identifier_or_keyword
    | string_literal
    | interpolated_string_literal
    | '(' verbatim_balanced_text ')'
    | '[' verbatim_balanced_text ']'
    | '{' verbatim_balanced_text '}'
    ;
    
single_verbatim_balanced_text_character
    : '<Any character except / (U+002F), @ (U+0040), \" (U+0022), $ (U+0024), ( (U+0028), ) (U+0029), [ (U+005B), ] (U+005D), { (U+007B) and } (U+007D)>'
    | '</ (U+002F), if not directly followed by / (U+002F) or * (U+002A)>'
    ;

Interpolated_string_literal token 會重新解譯為多個權杖和其他輸入元素,如下所示: interpolated_string_literal 的出現順序:An interpolated_string_literal token is reinterpreted as multiple tokens and other input elements as follows, in order of occurrence in the interpolated_string_literal:

  • 下列專案會重新解譯為個別的個別標記:開頭的 $ 正負號、 interpolated_regular_string_wholeinterpolated_regular_string_startinterpolated_regular_string_midinterpolated_regular_string_endinterpolated_verbatim_string_wholeinterpolated_verbatim_string_startinterpolated_verbatim_string_midinterpolated_verbatim_string_endOccurrences of the following are reinterpreted as separate individual tokens: the leading $ sign, interpolated_regular_string_whole, interpolated_regular_string_start, interpolated_regular_string_mid, interpolated_regular_string_end, interpolated_verbatim_string_whole, interpolated_verbatim_string_start, interpolated_verbatim_string_mid and interpolated_verbatim_string_end.
  • 在這些專案之間的 regular_balanced_textverbatim_balanced_text 會重新處理為 input_section (詞法分析) ,並重新解譯為輸入元素的結果序列。Occurrences of regular_balanced_text and verbatim_balanced_text between these are reprocessed as an input_section (Lexical analysis) and are reinterpreted as the resulting sequence of input elements. 這些可能會包含要重新解譯的插入字串常值標記。These may in turn include interpolated string literal tokens to be reinterpreted.

語法分析會將權杖重新合併) 中的 interpolated_string_expression (插入 字串Syntactic analysis will recombine the tokens into an interpolated_string_expression (Interpolated strings).

範例 TODOExamples TODO

Null 常值The null literal

null_literal
    : 'null'
    ;

Null_literal 可以隱含地轉換成參考型別或可為 null 的型別。The null_literal can be implicitly converted to a reference type or nullable type.

運算子和標點符號Operators and punctuators

有數種運算子和標點符號。There are several kinds of operators and punctuators. 運算子會在運算式中用來描述涉及一或多個運算元的作業。Operators are used in expressions to describe operations involving one or more operands. 例如,a + b 運算式會使用 + 運算子,將 ab 兩個運算元相加。For example, the expression a + b uses the + operator to add the two operands a and b. 標點符號用於分組和分隔。Punctuators are for grouping and separating.

operator_or_punctuator
    : '{'  | '}'  | '['  | ']'  | '('   | ')'  | '.'  | ','  | ':'  | ';'
    | '+'  | '-'  | '*'  | '/'  | '%'   | '&'  | '|'  | '^'  | '!'  | '~'
    | '='  | '<'  | '>'  | '?'  | '??'  | '::' | '++' | '--' | '&&' | '||'
    | '->' | '==' | '!=' | '<=' | '>='  | '+=' | '-=' | '*=' | '/=' | '%='
    | '&=' | '|=' | '^=' | '<<' | '<<=' | '=>'
    ;

right_shift
    : '>>'
    ;

right_shift_assignment
    : '>>='
    ;

Right_shiftright_shift_assignment 生產中的垂直線可用來指出,與語法文法中的其他生產不同的是,不允許在權杖之間使用任何種類的字元 (甚至空白字元) 。The vertical bar in the right_shift and right_shift_assignment productions are used to indicate that, unlike other productions in the syntactic grammar, no characters of any kind (not even whitespace) are allowed between the tokens. 這些生產會特別處理,以啟用 type_parameter_list s (類型參數) 的正確處理。These productions are treated specially in order to enable the correct handling of type_parameter_list s (Type parameters).

前置處理指示詞Pre-processing directives

前置處理指示詞可讓您有條件地略過原始程式檔的區段、報告錯誤和警告狀況,以及描繪不同的原始程式碼區域。The pre-processing directives provide the ability to conditionally skip sections of source files, to report error and warning conditions, and to delineate distinct regions of source code. 「前置處理指示程式」一詞僅用來與 C 和 c + + 程式設計語言一致。The term "pre-processing directives" is used only for consistency with the C and C++ programming languages. C # 中沒有個別的前置處理步驟;前置處理指示詞會在「詞彙分析」階段中處理。In C#, there is no separate pre-processing step; pre-processing directives are processed as part of the lexical analysis phase.

pp_directive
    : pp_declaration
    | pp_conditional
    | pp_line
    | pp_diagnostic
    | pp_region
    | pp_pragma
    ;

以下是可用的前置處理指示詞:The following pre-processing directives are available:

  • #define#undef ,分別用來定義和取消定義條件式編譯符號, (宣告指示詞) 。#define and #undef, which are used to define and undefine, respectively, conditional compilation symbols (Declaration directives).
  • #if#elif#else#endif ,可用來有條件地略過原始程式碼區段 (條件式 編譯 指示詞) 。#if, #elif, #else, and #endif, which are used to conditionally skip sections of source code (Conditional compilation directives).
  • #line,用來控制針對錯誤和警告發出的行號 ( 指示詞) 。#line, which is used to control line numbers emitted for errors and warnings (Line directives).
  • #error#warning ,分別用來發出錯誤和警告,分別 (診斷 指示詞) 。#error and #warning, which are used to issue errors and warnings, respectively (Diagnostic directives).
  • #region#endregion ,用來明確地將原始程式碼區段標示 (區域 指示詞) 。#region and #endregion, which are used to explicitly mark sections of source code (Region directives).
  • #pragma,用來指定編譯器 (Pragma 指示詞) 的選擇性內容資訊。#pragma, which is used to specify optional contextual information to the compiler (Pragma directives).

前置處理指示詞一律會佔用個別的源程式碼,且一律以 # 字元和前置處理指示詞名稱開頭。A pre-processing directive always occupies a separate line of source code and always begins with a # character and a pre-processing directive name. 空白字元可能會在字元之前 # 以及字元和指示詞名稱之間發生 #White space may occur before the # character and between the # character and the directive name.

包含、、、、、、或指示詞的原始程式列, #define #undef #if #elif #else #endif #line #endregion 可能會以單行批註結尾。A source line containing a #define, #undef, #if, #elif, #else, #endif, #line, or #endregion directive may end with a single-line comment. (/* */ 批註樣式的分隔批註,在包含前置處理指示詞的來源行上不允許使用) 。Delimited comments (the /* */ style of comments) are not permitted on source lines containing pre-processing directives.

前置處理指示詞不是標記,也不是 c # 語法文法的一部分。Pre-processing directives are not tokens and are not part of the syntactic grammar of C#. 不過,前置處理指示詞可用來包含或排除 token 的順序,而且在這種情況下會影響 c # 程式的意義。However, pre-processing directives can be used to include or exclude sequences of tokens and can in that way affect the meaning of a C# program. 例如,在編譯時,程式會:For example, when compiled, the program:

#define A
#undef B

class C
{
#if A
    void F() {}
#else
    void G() {}
#endif

#if B
    void H() {}
#else
    void I() {}
#endif
}

產生與程式完全相同的標記順序:results in the exact same sequence of tokens as the program:

class C
{
    void F() {}
    void I() {}
}

因此,雖然這兩個程式在語法上是相當不同的,但它們是完全相同的。Thus, whereas lexically, the two programs are quite different, syntactically, they are identical.

條件式編譯的符號Conditional compilation symbols

、、和指示詞所提供的條件式編譯功能 #if#elif #else #endif 是透過前置處理運算式來控制, (前置處理運算式) 和條件式編譯符號。The conditional compilation functionality provided by the #if, #elif, #else, and #endif directives is controlled through pre-processing expressions (Pre-processing expressions) and conditional compilation symbols.

conditional_symbol
    : '<Any identifier_or_keyword except true or false>'
    ;

條件式編譯符號有兩種可能的狀態: *定義 _ 或 _ 未定義 *。A conditional compilation symbol has two possible states: defined _ or _undefined**. 在原始程式檔的詞法處理開始時,除非已明確地定義了外部機制 ((例如命令列編譯器選項) ),否則不會定義條件式編譯符號。At the beginning of the lexical processing of a source file, a conditional compilation symbol is undefined unless it has been explicitly defined by an external mechanism (such as a command-line compiler option). 處理指示詞時,會在該指示詞中 #define 指定的條件式編譯符號會在該原始程式檔中定義。When a #define directive is processed, the conditional compilation symbol named in that directive becomes defined in that source file. 符號會保持定義,直到 #undef 處理相同符號的指示詞,或到達原始程式檔的結尾為止。The symbol remains defined until an #undef directive for that same symbol is processed, or until the end of the source file is reached. 這表示 #define#undef 在一個原始程式檔中,和指示詞不會影響相同程式中的其他原始程式檔。An implication of this is that #define and #undef directives in one source file have no effect on other source files in the same program.

在前置處理運算式中參考時,定義的條件式編譯符號具有布林值 true ,且未定義的條件式編譯符號具有布林值 falseWhen referenced in a pre-processing expression, a defined conditional compilation symbol has the boolean value true, and an undefined conditional compilation symbol has the boolean value false. 在前置處理運算式中參考條件式編譯符號之前,不需要明確宣告這些符號。There is no requirement that conditional compilation symbols be explicitly declared before they are referenced in pre-processing expressions. 相反地,未宣告的符號則是未定義的,因此會有值 falseInstead, undeclared symbols are simply undefined and thus have the value false.

條件式編譯符號的名稱空間與 c # 程式中的所有其他已命名實體不同,而且不同。The name space for conditional compilation symbols is distinct and separate from all other named entities in a C# program. 條件式編譯符號只能在和指示 #define#undef 以及前置處理運算式中參考。Conditional compilation symbols can only be referenced in #define and #undef directives and in pre-processing expressions.

前置處理運算式Pre-processing expressions

前置處理運算式可能會出現在和指示詞中 #if #elifPre-processing expressions can occur in #if and #elif directives. ! == != && || 前置處理運算式中允許運算子、、和,而括弧可用於群組。The operators !, ==, !=, && and || are permitted in pre-processing expressions, and parentheses may be used for grouping.

pp_expression
    : whitespace? pp_or_expression whitespace?
    ;

pp_or_expression
    : pp_and_expression
    | pp_or_expression whitespace? '||' whitespace? pp_and_expression
    ;

pp_and_expression
    : pp_equality_expression
    | pp_and_expression whitespace? '&&' whitespace? pp_equality_expression
    ;

pp_equality_expression
    : pp_unary_expression
    | pp_equality_expression whitespace? '==' whitespace? pp_unary_expression
    | pp_equality_expression whitespace? '!=' whitespace? pp_unary_expression
    ;

pp_unary_expression
    : pp_primary_expression
    | '!' whitespace? pp_unary_expression
    ;

pp_primary_expression
    : 'true'
    | 'false'
    | conditional_symbol
    | '(' whitespace? pp_expression whitespace? ')'
    ;

在前置處理運算式中參考時,定義的條件式編譯符號具有布林值 true ,且未定義的條件式編譯符號具有布林值 falseWhen referenced in a pre-processing expression, a defined conditional compilation symbol has the boolean value true, and an undefined conditional compilation symbol has the boolean value false.

預先處理運算式的評估一律會產生布林值。Evaluation of a pre-processing expression always yields a boolean value. 預先處理運算式的評估規則與常數運算式 (常數運算式) 相同,不同之處在于唯一可參考的使用者定義實體是條件式編譯符號。The rules of evaluation for a pre-processing expression are the same as those for a constant expression (Constant expressions), except that the only user-defined entities that can be referenced are conditional compilation symbols.

宣告指示詞Declaration directives

宣告指示詞可用來定義或取消定義條件式編譯符號。The declaration directives are used to define or undefine conditional compilation symbols.

pp_declaration
    : whitespace? '#' whitespace? 'define' whitespace conditional_symbol pp_new_line
    | whitespace? '#' whitespace? 'undef' whitespace conditional_symbol pp_new_line
    ;

pp_new_line
    : whitespace? single_line_comment? new_line
    ;

指示詞的處理 #define 會使指定的條件式編譯符號變成已定義,從指示詞後面的原始程式列開始。The processing of a #define directive causes the given conditional compilation symbol to become defined, starting with the source line that follows the directive. 同樣地,處理指示詞 #undef 會使指定的條件式編譯符號變成未定義,從指示詞後面的原始程式列開始。Likewise, the processing of an #undef directive causes the given conditional compilation symbol to become undefined, starting with the source line that follows the directive.

#define來源檔案中的任何和指示詞必須在原始檔 #undef 中) 第一個 權杖 (標記之前,否則會發生編譯時期錯誤。Any #define and #undef directives in a source file must occur before the first token (Tokens) in the source file; otherwise a compile-time error occurs. 以直覺的方式,和指示詞必須在原始程式檔 #define #undef 中的任何 "real code" 之前。In intuitive terms, #define and #undef directives must precede any "real code" in the source file.

範例:The example:

#define Enterprise

#if Professional || Enterprise
    #define Advanced
#endif

namespace Megacorp.Data
{
    #if Advanced
    class PivotTable {...}
    #endif
}

有效,因為指示詞在 #define 第一個 token 之前 (namespace 關鍵字) 在來源檔案中。is valid because the #define directives precede the first token (the namespace keyword) in the source file.

下列範例會產生編譯時期錯誤,因為以下是實際的程式 #define 代碼:The following example results in a compile-time error because a #define follows real code:

#define A
namespace N
{
    #define B
    #if B
    class Class1 {}
    #endif
}

#define可以定義已定義的條件式編譯符號,而不會有該符號的任何中間 #undefA #define may define a conditional compilation symbol that is already defined, without there being any intervening #undef for that symbol. 下列範例會定義條件式編譯符號 A ,然後再次定義。The example below defines a conditional compilation symbol A and then defines it again.

#define A
#define A

可以「取消定義」 #undef 未定義的條件式編譯符號。A #undef may "undefine" a conditional compilation symbol that is not defined. 下列範例會定義條件式編譯符號 A ,然後將它重新定義為兩次; 雖然第二個沒有 #undef 任何作用,但它仍然是有效的。The example below defines a conditional compilation symbol A and then undefines it twice; although the second #undef has no effect, it is still valid.

#define A
#undef A
#undef A

條件式編譯指示詞Conditional compilation directives

條件式編譯指示詞可用來有條件地包含或排除來源檔案的部分。The conditional compilation directives are used to conditionally include or exclude portions of a source file.

pp_conditional
    : pp_if_section pp_elif_section* pp_else_section? pp_endif
    ;

pp_if_section
    : whitespace? '#' whitespace? 'if' whitespace pp_expression pp_new_line conditional_section?
    ;

pp_elif_section
    : whitespace? '#' whitespace? 'elif' whitespace pp_expression pp_new_line conditional_section?
    ;

pp_else_section:
    | whitespace? '#' whitespace? 'else' pp_new_line conditional_section?
    ;

pp_endif
    : whitespace? '#' whitespace? 'endif' pp_new_line
    ;

conditional_section
    : input_section
    | skipped_section
    ;

skipped_section
    : skipped_section_part+
    ;

skipped_section_part
    : skipped_characters? new_line
    | pp_directive
    ;

skipped_characters
    : whitespace? not_number_sign input_character*
    ;

not_number_sign
    : '<Any input_character except #>'
    ;

如語法所指示,條件式編譯指示詞必須撰寫為由、order、指示詞 #if 、零個或多個指示詞、零個或一個指示詞, #elif #else 以及一個 #endif 指示詞所組成的集合。As indicated by the syntax, conditional compilation directives must be written as sets consisting of, in order, an #if directive, zero or more #elif directives, zero or one #else directive, and an #endif directive. 指示詞之間的準則是原始程式碼的條件區段。Between the directives are conditional sections of source code. 每個區段都是由緊接在前面的指示詞所控制。Each section is controlled by the immediately preceding directive. 條件式區段本身可以包含內嵌的條件式編譯指示詞,提供這些指示詞形成完整集合。A conditional section may itself contain nested conditional compilation directives provided these directives form complete sets.

Pp_conditional 最多選取其中一個包含的 conditional_section 來進行一般的詞法處理:A pp_conditional selects at most one of the contained conditional_section s for normal lexical processing:

  • 和指示詞的 pp_expression #if #elif 會依序進行評估,直到其中一個結果為止 trueThe pp_expression s of the #if and #elif directives are evaluated in order until one yields true. 如果運算式產生,則會選取對應之指示詞的 true conditional_sectionIf an expression yields true, the conditional_section of the corresponding directive is selected.
  • 如果所有 pp_expression 都產生 false ,而指示詞 #else 存在,則會選取指示詞的 conditional_section #elseIf all pp_expression s yield false, and if an #else directive is present, the conditional_section of the #else directive is selected.
  • 否則,就不會選取任何 conditional_sectionOtherwise, no conditional_section is selected.

選取的 conditional_section(如果有的話)會處理為一般 input_section:區段中包含的原始程式碼必須符合詞法文法;權杖是從一節中的原始程式碼產生的;區段中的前置處理指示詞具有指定的效果。The selected conditional_section, if any, is processed as a normal input_section: the source code contained in the section must adhere to the lexical grammar; tokens are generated from the source code in the section; and pre-processing directives in the section have the prescribed effects.

其餘的 conditional_section s (如果有的話)會處理為 skipped_section s:除了前置處理指示詞之外,區段中的原始程式碼不需要遵守詞法文法;不會從一節中的原始程式碼產生任何權杖;區段中的前置處理指示詞必須是正確的,但不會進行處理。The remaining conditional_section s, if any, are processed as skipped_section s: except for pre-processing directives, the source code in the section need not adhere to the lexical grammar; no tokens are generated from the source code in the section; and pre-processing directives in the section must be lexically correct but are not otherwise processed. 在處理為 skipped_sectionconditional_section 中,任何嵌套的 conditional_section s (包含于 nested 和 ... #if #endif #region #endregion 結構) 也會以 skipped_section 的方式處理。Within a conditional_section that is being processed as a skipped_section, any nested conditional_section s (contained in nested #if...#endif and #region...#endregion constructs) are also processed as skipped_section s.

下列範例說明條件式編譯指示詞如何進行嵌套:The following example illustrates how conditional compilation directives can nest:

#define Debug       // Debugging on
#undef Trace        // Tracing off

class PurchaseTransaction
{
    void Commit() {
        #if Debug
            CheckConsistency();
            #if Trace
                WriteToLog(this.ToString());
            #endif
        #endif
        CommitHelper();
    }
}

除了前置處理指示詞之外,略過的原始程式碼不會受限於詞法分析。Except for pre-processing directives, skipped source code is not subject to lexical analysis. 例如,雖然區段中未結束的批註,下列仍有效 #elseFor example, the following is valid despite the unterminated comment in the #else section:

#define Debug        // Debugging on

class PurchaseTransaction
{
    void Commit() {
        #if Debug
            CheckConsistency();
        #else
            /* Do something else
        #endif
    }
}

不過請注意,即使在原始程式碼的略過區段中,前置處理指示詞也必須是正確的。Note, however, that pre-processing directives are required to be lexically correct even in skipped sections of source code.

前置處理指示詞在多行輸入元素中出現時,不會處理。Pre-processing directives are not processed when they appear inside multi-line input elements. 例如,程式:For example, the program:

class Hello
{
    static void Main() {
        System.Console.WriteLine(@"hello, 
#if Debug
        world
#else
        Nebraska
#endif
        ");
    }
}

輸出結果:results in the output:

hello,
#if Debug
        world
#else
        Nebraska
#endif

在什麼嗎的情況下,所處理的前置處理指示程式集可能取決於 pp_expression 的評估。In peculiar cases, the set of pre-processing directives that is processed might depend on the evaluation of the pp_expression. 範例:The example:

#if X
    /*
#else
    /* */ class Q { }
#endif

不論是否已定義,一律會產生相同的 token 資料流程 (class Q { }) Xalways produces the same token stream (class Q { }), regardless of whether or not X is defined. 如果 X 已定義,則唯一處理的指示詞為 #if#endif ,因為多行批註。If X is defined, the only processed directives are #if and #endif, due to the multi-line comment. 如果 X 未定義,則三個指示詞 (#if#else) 是指示詞集合的 #endif 一部分。If X is undefined, then three directives (#if, #else, #endif) are part of the directive set.

診斷指示詞Diagnostic directives

診斷指示詞可用來明確產生錯誤和警告訊息,這些訊息的報告方式與其他編譯時期錯誤和警告相同。The diagnostic directives are used to explicitly generate error and warning messages that are reported in the same way as other compile-time errors and warnings.

pp_diagnostic
    : whitespace? '#' whitespace? 'error' pp_message
    | whitespace? '#' whitespace? 'warning' pp_message
    ;

pp_message
    : new_line
    | whitespace input_character* new_line
    ;

範例:The example:

#warning Code review needed before check-in

#if Debug && Retail
    #error A build can't be both debug and retail
#endif

class Test {...}

一律會產生警告 ( 「簽入前所需的程式碼審核」 ) ,並產生編譯時期錯誤 ( 「組建不能同時是 debug 和 retail」 ) 如果條件式符號 Debug 和都 Retail 已定義的話。always produces a warning ("Code review needed before check-in"), and produces a compile-time error ("A build can't be both debug and retail") if the conditional symbols Debug and Retail are both defined. 請注意, pp_message 可以包含任意文字;具體而言,它不需要包含格式正確的標記,如字組中的單引號所示 can'tNote that a pp_message can contain arbitrary text; specifically, it need not contain well-formed tokens, as shown by the single quote in the word can't.

Region 指示詞Region directives

區域指示詞可用來明確地標示原始程式碼的區域。The region directives are used to explicitly mark regions of source code.

pp_region
    : pp_start_region conditional_section? pp_end_region
    ;

pp_start_region
    : whitespace? '#' whitespace? 'region' pp_message
    ;

pp_end_region
    : whitespace? '#' whitespace? 'endregion' pp_message
    ;

未將任何語義意義附加至區域;區域是由程式設計人員或自動化工具所使用,可標示原始程式碼的某個區段。No semantic meaning is attached to a region; regions are intended for use by the programmer or by automated tools to mark a section of source code. 或指示詞中指定的訊息 #region #endregion 同樣沒有語義意義,只是用來識別區域。The message specified in a #region or #endregion directive likewise has no semantic meaning; it merely serves to identify the region. #region 對和指示詞 #endregion 可能會有不同的 pp_message s。Matching #region and #endregion directives may have different pp_message s.

區域的詞法處理:The lexical processing of a region:

#region
...
#endregion

完全對應至下列格式的條件式編譯指示詞的詞法處理:corresponds exactly to the lexical processing of a conditional compilation directive of the form:

#if true
...
#endif

Line 指示詞Line directives

Line 指示詞可用來改變編譯器在輸出中報告的行號和原始程式檔名稱(例如警告和錯誤),以及呼叫端資訊屬性 (呼叫 端資訊屬性) 所使用的行號和來原始檔案名。Line directives may be used to alter the line numbers and source file names that are reported by the compiler in output such as warnings and errors, and that are used by caller info attributes (Caller info attributes).

Line 指示詞最常用於中繼程式設計工具,從其他文字輸入產生 c # 原始程式碼。Line directives are most commonly used in meta-programming tools that generate C# source code from some other text input.

pp_line
    : whitespace? '#' whitespace? 'line' whitespace line_indicator pp_new_line
    ;

line_indicator
    : decimal_digit+ whitespace file_name
    | decimal_digit+
    | 'default'
    | 'hidden'
    ;

file_name
    : '"' file_name_character+ '"'
    ;

file_name_character
    : '<Any input_character except ">'
    ;

當沒有任何指示詞時 #line ,編譯器會在其輸出中報告真正的行號和原始程式檔名稱。When no #line directives are present, the compiler reports true line numbers and source file names in its output. 當處理 #line 包含不 line_indicator 的指示詞時,編譯器會將指示詞 default 後面的那一行視為具有指定的行號 (和檔案名,如果有指定) 。When processing a #line directive that includes a line_indicator that is not default, the compiler treats the line after the directive as having the given line number (and file name, if specified).

指示詞會 #line default 反轉所有前面 #line 指示詞的效果。A #line default directive reverses the effect of all preceding #line directives. 編譯器會針對後續的程式碼報告真正的行資訊,精確地如同未處理任何指示詞 #lineThe compiler reports true line information for subsequent lines, precisely as if no #line directives had been processed.

指示詞 #line hidden 不會影響錯誤訊息中所報告的檔案和行號,但會影響來源層級的偵錯工具。A #line hidden directive has no effect on the file and line numbers reported in error messages, but does affect source level debugging. 進行偵錯工具時,指示詞和後續指示詞之間的所有程式程式碼 #line hidden #line (不 #line hidden) 都沒有行號資訊。When debugging, all lines between a #line hidden directive and the subsequent #line directive (that is not #line hidden) have no line number information. 在偵錯工具中逐步執行程式碼時,將會完全略過這些行。When stepping through code in the debugger, these lines will be skipped entirely.

請注意, file_name 與一般字串常值不同,因為不會處理該 escape 字元;「」 \ 字元只會指定 file_name 內的一般反斜線字元。Note that a file_name differs from a regular string literal in that escape characters are not processed; the "\" character simply designates an ordinary backslash character within a file_name.

Pragma 指示詞Pragma directives

前置處理指示詞 #pragma 可用來指定編譯器的選擇性內容資訊。The #pragma preprocessing directive is used to specify optional contextual information to the compiler. 指示詞中提供的資訊 #pragma 永遠不會變更程式語義。The information supplied in a #pragma directive will never change program semantics.

pp_pragma
    : whitespace? '#' whitespace? 'pragma' whitespace pragma_body pp_new_line
    ;

pragma_body
    : pragma_warning_body
    ;

C # 提供指示詞 #pragma 來控制編譯器警告。C# provides #pragma directives to control compiler warnings. 語言的未來版本可能會包含其他指示詞 #pragmaFuture versions of the language may include additional #pragma directives. 為了確保與其他 c # 編譯器的互通性,Microsoft c # 編譯器不會發出未知指示詞的編譯錯誤 #pragma ; 不過,這類指示詞會產生警告。To ensure interoperability with other C# compilers, the Microsoft C# compiler does not issue compilation errors for unknown #pragma directives; such directives do however generate warnings.

Pragma 警告Pragma warning

指示詞 #pragma warning 是用來在編譯後續的程式文字期間,停用或還原所有或一組特定的警告訊息。The #pragma warning directive is used to disable or restore all or a particular set of warning messages during compilation of the subsequent program text.

pragma_warning_body
    : 'warning' whitespace warning_action
    | 'warning' whitespace warning_action whitespace warning_list
    ;

warning_action
    : 'disable'
    | 'restore'
    ;

warning_list
    : decimal_digit+ (whitespace? ',' whitespace? decimal_digit+)*
    ;

#pragma warning省略警告清單的指示詞會影響所有的警告。A #pragma warning directive that omits the warning list affects all warnings. #pragma warning包含警告清單的指示詞只會影響清單中所指定的警告。A #pragma warning directive that includes a warning list affects only those warnings that are specified in the list.

指示詞會 #pragma warning disable 停用所有或指定的一組警告。A #pragma warning disable directive disables all or the given set of warnings.

指示詞 #pragma warning restore 會將所有或指定的一組警告還原至編譯單位開始生效的狀態。A #pragma warning restore directive restores all or the given set of warnings to the state that was in effect at the beginning of the compilation unit. 請注意,如果在外部停用特定警告, #pragma warning restore (是否要針對所有或特定的警告) 不會重新啟用該警告。Note that if a particular warning was disabled externally, a #pragma warning restore (whether for all or the specific warning) will not re-enable that warning.

下列範例示範 #pragma warning 如何使用 Microsoft c # 編譯器中的警告編號,以暫時停用在參考過時成員時所報告的警告。The following example shows use of #pragma warning to temporarily disable the warning reported when obsoleted members are referenced, using the warning number from the Microsoft C# compiler.

using System;

class Program
{
    [Obsolete]
    static void Foo() {}

    static void Main() {
#pragma warning disable 612
    Foo();
#pragma warning restore 612
    }
}