C# 程序(PRogram)由至少一个源文件(source files)组成,其正式称谓为编译单元(compilation units)[1]。每个源文件都是有序的 Unicode 字符序列。源文件通常与文件系统内的相应文件具有一对一关系,但这种相关性并非必须因素。为尽最大可能确保可移植性,推荐文件系统中的文件编码为 UTF-8 编码规范。
从理论上来说,程序编译由三步骤组成:
本规范提出两种文法(grammars)来表示 C# 的语法(syntax)。词法文法(lexical grammar)定义了 Unicode 字符是如何组成行结束符(line terminators)、空白(white space)、注释(comments)、标记(tokens)和预处理指令(pre-processing directives)的,关于这一方面请详见本规范第二章第 2.2 节;句法文法(syntactic grammar)则定义了标记符号是如何从词法文法(lexical grammar)转换而来并组成了 C# 程序的,这个则将在本规范第二章第 2.3 节详述。
词法文法(lexical grammar)和句法文法(syntactic grammar)均通过使用文法产生式(grammar productions)来表示。每个文法产生式都定义了一个非终结符号(non-terminal symbol)及其可能的扩展(扩展由非终结符号与终结符号组成)。在文法产生式中,非终结符号被用斜体显示,而终结符号则使用等宽字体(fixed-width font)来表示。
词法产生式的第一行被定义为非终结符号的名称,而后紧跟着一个冒号 :
。紧接这一行之后的连续几行相同缩进风格的是由非终结符号或终结符号构成的扩展。比方说下面这个词法产生式:
上面定义的这个 while-statement
包含了一个 while
标记,之后分别跟着 (
、boolean-expression
、)
和 embedded-statement
。当有超过一个非终结符扩展时,将它们交替列出,每一个扩展独占一行。如下面这个词法产生式:
上面定义的 statement-list
包含了一个 statement
以及一个紧随其后的 statement-list
。换句话说,所定义的 statement-list
是递归的,它包含至少一个 statement
。
下标 “opt” 被用于标记可选符号(optional symbol)。下面这个词法产生式,
是下面这个词法产生式的缩写形式:
它定义了一个区块,用大括号标记 {...}
包裹起一个可选的 statement-list
。
像这种每行一个进行列举选项的形式,也可以通过使用 one of
将这些扩展列表写进一行里。也就是说这是一种在一行中显示这些选项的缩写形式。比方说下面这个词法产生式,
它的缩写形式如下:
关于词法文法(lexical grammar)的内容请参见第二章第三节、第四节和第五节。词法文法的终结符号(terminal symbols)使用 Unicode 字符集,并具体详述了字符是如何相互组合(combined)以构成标记(token,第二章第四节)、空白(white space,第二章第3.3节)以及预处理指令(pre-processing directives,第二章第五节)的。
每个 C# 程序的源文件都必须遵循词法文法的 input
产生式(见第二章第三节)。
句法文法(syntactic grammar)在本章以及之后的附录部分进行介绍。句法文法的终结符号(terminal symbols)由词法文法定义为标记,而句法文法则指定了这些标记是如何相互组合并构成 C# 程序的。
每一个 C# 程序的源文件都必须遵循句法文法 compilation-unit
产生式(见第九章第一节)。
input
产生式定义了 C# 源文件的词法结构。每个源文件都必须遵循这个词法文法产生式(lexical grammar production)。
C# 源文件的词法结构由行终结符(Line terminators,第二章第3.1节)、空白(white space,第二章第3.3节)、注释(comments,第二章第3.2节)、标记(tokens,第二章第四节)以及预处理指令(pre-processing directives,第二章第五节)这五种基础元素(basic elements)组成。对于这些基础元素而言,只有标记(tokens)才是对句法文法有意义的(关于这一点,详见第二章第2.3节)。
C# 源文件的词法处理包括简化文件为标记序列以便能输入并进行句法分析。行终结符、空白和注释用于分割每一个标记,而预处理指令能导致跳过源文件的某些区段,然而这些词法元素(lexical elements)并不会影响到 C# 程序的句法结构。
当多个词法文法产生式匹配到同一个源文件字符序列,词法处理会尽力构成最长的词法元素。字符序列 //
被处理为单行注释的开头,因为其词法元素比单个斜杠 /
标记要长。
行终结符(line terminators)将 C# 源文件的字符分割为多行。
为了与增加有 EOF 标记(end-of-file markers
)的源代码编辑工具兼容,以及确保能以正确的行终结序列查看源代码,下列转换(transformations)将按顺序应用于 C# 程序的每一个源文件:
Control-Z
(U+001A),此字符将被删除;carriage-return character
(U+000D)、换行符 line feed
(U+000A)、行分隔符 line separator
(U+2028)或段落分隔符 paragraph separator
(U+2029)的话,回车符 carriage-return character
(U+000D)将被添加到源文件的最后位置。支持两种形式的注释:单行注释(single-line comments)和分割注释(delimited comments)。单行注释以字符 //
起始、延续并终于该行之尾。分割注释始于字符 /*
并止于字符 */
,分割注释支持跨行。
注释不可嵌套(nest)。字符序列 /*
与 */
置于 //
内部亦或是字符序列 //
和 /*
置于分割注释内部均毫无意义。置于字符与字符串内部的注释不会被处理。在例子
/* Hello, world program This program writes “hello, world” to the console*/class Hello{ static void Main() { System.Console.WriteLine("hello, world"); }}
中,包含一个跨行注释。在例子
// 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"); }}
中则展示了多个单行注释。
空白(white space)的定义包含了所有 Unicode Zs 集字符[2](包括空白字符 space character)、水平制表符(horizontal tab character)、垂直制表符(vertical tab character)和换页符(form feed character)。
有以下几种标记(tokens):标识符(identifiers)、关键字(keyWords)、文本(literals)、操作符(Operators)和标点符号(punctuators)。空白(white space)与注释(comments)并非标记,它们只是标记的分隔符。
Unicode 字符转义序列(Unicode character escape sequence)表示 Unicode 字符。Unicode 字符转义序列处理于标识符(第二章第4.2节)、字符文本(character literals,第二章第4.4.4节)以及正则字符串文本(regular string literals,第二章第4.4.5节)内。除此以外,Unicode 字符转义不会在其他任何地方被处理(比如在构成操作符、标点符或关键字时)。
Unicode 转义序列用以 /u
或 /U
开头、续接一个十六进制数的字符形式来表示单个 Unicode 字符。因 C# 字符与字符串使用 16 位(16-bit)编码 Unicode 字符点,故区间在 U+10000
与 U+10FFFF
之间的 Unicode 字符不能在 C# 字符中使用;在字符串中则使用一个代理对(surrogate pair)来表示。不支持代码点在 0x10FFFF
以上的 Unicode 字符。
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()); }}
区段(section)内的标识符规则与 Unicode 规范附录 31 所推荐的一致,除了以下情况:
_
允许用作初始字符(initial character,C 语言的一贯做法);@
符号可以用于关键字的前缀以便使其可为标识符。上面所提及的 Unicode 字符集(Unicode character classes)仅供参考,具体请参见 Unicode 规范(版本 3.0,第 4.5 节)。
有效的标识符包括 identifier1
、_identifier2
和 @if
。
在一个合格的程序中的标识符耶必须符合 Unicode Normalization Form C 的规范(定义于 Unicode 规范附录 15)。当遇到一个不符合上述规范的标识符时,(如何处理)可由实现自行具体定义(implementation-defined),但不强制要求诊断(diagnostic)。
添加有前缀 @
的关键字(keywords)可以成为一个标识符(identifiers),此举在与其他编程语言配合时尤为有用。字符 @
并非标识符的实际组成部分,故其它语言可将其(标识符)视为一个不带前缀的普通标识符。带有前缀 @
的标识符被称为逐字标识符(verbatim identifier)。允许将 @
用作非关键字的标识符之
新闻热点
疑难解答