C# 程序 (program) 由一个或多个源文件 (source file) 组成,源文件的正式名称是编译单元 (compilation unit)。源文件是有序的 Unicode 字符序列。
源文件与文件系统中的文件通常具有一对一的对应关系,但这种对应关系不是必需的。为实现可移植性的最大化,建议这些文件在文件系统中应按 UTF-8 编码规范编码。
从概念上讲,程序的编译分三个步骤:
1> 转换 : 这一步将用特定字符指令系统和编码方案编写的文件转换为 Unicode 字符序列
2> 词法分析 : 这一步将 Unicode 输入字符流转换为标记流
3> 句法分析 : 这一步将标记流转换为可执行代码
本规范采用两种文法 (grammar) 来表示 C# 编程语言的语法 (syntax)。
词法文法 (lexical grammar)规定怎样将 Unicode 字符组合成行结束符、空白、注释、标记和预处理指令等。
句法文法 (syntactic grammar)规定如何将那些由词法文法产生的标记组合成 C# 程序。
词法文法和句法文法用文法产生式 (grammar production) 来表示。每个文法产生式定义一个非结束符号和它可能的扩展(由非结束符或结束符组成的序列)。在文法产生式中,non-terminal 符号显示为斜体,而 terminal 符号显示为等宽字体。
文法产生式的第一行是该产生式所定义的非结束符号的名称,后跟一个冒号。每个后续的缩进行列出一个可能的扩展,它是以非结束符或结束符组成的序列的形式给出的。
例如,产生式:
while-statement:
while(boolean-expression) embedded-statement
定义了一个 while-statement,它是这样构成的:由标记 while 开始,后跟标记“(”、boolean-expression、标记“)”,最后是一个 embedded-statement。
当有不止一个可能的非结束符号扩展时,列出这些可能的扩展(每个扩展单独占一行)。
例如,产生式:
statement-list:
statement
statement-list statement
定义一个 statement-list,它或仅含有一个 statement,或由一个 statement-list 和随后跟着的一个 statement 组成。换言之,定义是递归的,语句列表由一个或多个语句组成。
一个符号若以下标“[opt]”作其后缀,就表明该符号是可选的。
产生式:
block:
{
statement-list[opt] }
是以下产生式的简短形式:
block:
{
}
{
statement-list }
它定义了一个 block,此块由一个用“{”和“}”标记括起来的可选 statement-list 组成。
可选项通常在单独的行上列出,但是当有许多可选项时,可以在单行上给定的扩展列表之后加上短语“之一”。这只是在单独一行上列出每个可选项的简短形式。
例如,产生式:
real-type-suffix: one of
F f D d M m
是以下产生式的简短形式:
real-type-suffix:
F
f
D
d
M
m
词法文法的结束符号为 Unicode 字符集的字符,并且词法文法指定如何组合字符以构成标记、空白、注释和预处理指令。
C# 程序中的每个源文件都必须符合词法文法的 input 产生式。
句法文法的结束符号是由词法文法定义的标记,句法文法指定如何组合这些标记以构成 C# 程序。
C# 程序中的每个源文件都必须符合句法文法的 compilation-unit 产生式。
input 产生式定义 C# 源文件的词法结构。C# 程序中的每个源文件都必须符合此词法文法产生式。
input:
input-section[opt]
input-section:
input-section-part
input-section input-section-part
input-section-part:
input-elements[opt] new-line
pp-directive
input-elements:
input-element
input-elements input-element
input-element:
whitespace
comment
token
C# 源文件的词法结构由五个基本元素组成:行结束符、空白、注释、标记和预处理指令。在这些基本元素中,只有标记在 C# 程序的句法文法中具有重要意义。
对 C# 源文件的词法处理就是将文件缩减成标记序列,该序列然后即成为句法分析的输入。行结束符、空白和注释可用于分隔标记,预处理指令可导致跳过源文件中的某些节,除此之外这些词法元素对 C# 程序的句法结构没有任何影响。
当有若干词法文法产生式与源文件中的一个字符序列匹配时,词法处理总是构成尽可能最长的词法元素。例如,字符序列 // 按单行注释的开头处理,这是因为该词法元素比一个 / 标记要长。
行结束符将 C# 源文件的字符划分为行。
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# 程序中的每个源文件:
<1> 如果源文件的最后一个字符为 Control-Z 字符 (U+001A),则删除此字符。
<2> 如果源文件非空并且源文件的最后一个字符不是回车符 (U+000D)、换行符 (U+000A)、行分隔符 (U+2028) 或段落分隔符 (U+2029),则将在源文件的结尾添加一个回车符 (U+000D)。
支持两种形式的注释:单行注释和带分隔符的注释。单行注释 (Single-line comment) 以字符 // 开头并延续到源行的结尾。带分隔符的注释 (Delimited comment) 以字符 /* 开头,以字符 */ 结束。带分隔符的注释可以跨多行。
comment:
single-line-comment
delimited-comment
single-line-comment:
// input-characters[opt]
input-characters:
input-character
input-characters 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-text[opt] asterisks /
delimited-comment-text:
delimited-comment-section
delimited-comment-text delimited-comment-section
delimited-comment-section:
/
asterisks[opt] not-slash-or-asterisk
asterisks:
*
asterisks *
not-slash-or-asterisk:
Any Unicode character except / or *
注释不嵌套。字符序列 /* 和 / 在 // 注释中没有任何特殊含义,字符序列 // 和 / 在带分隔符的注释中没有任何特殊含义。
在字符和字符串内不处理注释。
下面的示例:
/* Hello, world program
This program writes “hello, world” to the console
*/
class Hello
{
static void Main()
{
System.Console.WriteLine("hello, world");
}
}
演示了若干单行注释。
空白被定义为任何含 Unicode 类 Zs 的字符(包括空白字符)以及水平制表符、垂直制表符和换页符。
whitespace:
Any character with Unicode class Zs
Horizontal tab character (U+0009)
Vertical tab character (U+000B)
Form feed character (U+000C)
有几类标记:标识符、关键字、文本、运算符和标点符号。空白和注释不是标记,但它们可充当标记的分隔符。
token:
identifier
keyword
integer-literal
real-literal
character-literal
string-literal
operator-or-punctuator
Unicode 字符转义序列表示一个 Unicode 字符。Unicode 字符转义序列在标识符、字符和规则字符串中处理。不在其他任何位置处理 Unicode 字符转义(例如,在构成运算符、标点符号或关键字时)。
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 转义序列表示由“\u”或“\U”字符后面的十六进制数字构成的单个 Unicode 字符。由于 C# 在字符和字符串值中使用 Unicode 代码点的 16 位编码,因此从 U+10000 到 U+10FFFF 的 Unicode 字符不能在字符中使用,在字符串中则用一个 Unicode 代理项对来表示。不支持代码数据点在 0x10FFFF 以上的 Unicode 字符。
不执行多次转换。例如,字符串文本“\u005Cu005C”等同于“\u005C”,而不是“\”。Unicode 值 \u005C 是字符“\”。
下面的示例:
class Class1
{
static void Test(bool \u0066)
{
char c = '\u0066';
if (\u0066)
{
System.Console.WriteLine(c.ToString());
}
}
}
演示了 \u0066(它是字母“f”的转义序列)的一些用法。该程序等效于:
class Class1
{
static void Test(bool f)
{
char c = 'f';
if (f)
{
System.Console.WriteLine(c.ToString());
}
}
}
本节给出的标识符规则完全符合 Unicode 标准附件 31 推荐的规则,但以下情况除外:允许将下划线用作初始字符(这是 C 编程语言的传统),允许在标识符中使用 Unicode 转义序列,以及允许“@”字符作为前缀以使关键字能够用作标识符。
identifier:
available-identifier
@ identifier-or-keyword
available-identifier:
An identifier-or-keyword that is not a keyword
identifier-or