语句Statements

C#提供各种语句。C# provides a variety of statements. 对于在 C 和C++中进行了编程的开发人员,这些语句中的大部分将很熟悉。Most of these statements will be familiar to developers who have programmed in C and C++.

statement
    : labeled_statement
    | declaration_statement
    | embedded_statement
    ;

embedded_statement
    : block
    | empty_statement
    | expression_statement
    | selection_statement
    | iteration_statement
    | jump_statement
    | try_statement
    | checked_statement
    | unchecked_statement
    | lock_statement
    | using_statement
    | yield_statement
    | embedded_statement_unsafe
    ;

Embedded_statement非终止符用于在其他语句中显示的语句。The embedded_statement nonterminal is used for statements that appear within other statements. 使用embedded_statement而不是语句不包括在这些上下文中声明语句和标记语句的使用。The use of embedded_statement rather than statement excludes the use of declaration statements and labeled statements in these contexts. 示例The example

void F(bool b) {
    if (b)
        int i = 44;
}

导致编译时错误,因为 if 语句需要embedded_statement而不是语句用于 if 分支。results in a compile-time error because an if statement requires an embedded_statement rather than a statement for its if branch. 如果允许此代码,则将声明变量 i,但绝不能使用它。If this code were permitted, then the variable i would be declared, but it could never be used. 但请注意,通过将 i的声明放置在块中,该示例是有效的。Note, however, that by placing i's declaration in a block, the example is valid.

终结点和可访问性End points and reachability

每个语句都有一个终结点Every statement has an end point. 简而言之,语句的结束点是紧跟在语句之后的位置。In intuitive terms, the end point of a statement is the location that immediately follows the statement. 复合语句的执行规则(包含嵌入语句的语句)指定在控件到达嵌入语句的终结点时所采取的操作。The execution rules for composite statements (statements that contain embedded statements) specify the action that is taken when control reaches the end point of an embedded statement. 例如,当控件到达块中语句的终点时,控制将转移到块中的下一条语句。For example, when control reaches the end point of a statement in a block, control is transferred to the next statement in the block.

如果语句可以通过执行到达,则表明该语句是可访问的。If a statement can possibly be reached by execution, the statement is said to be reachable. 相反,如果不可能执行语句,则认为该语句是无法访问的。Conversely, if there is no possibility that a statement will be executed, the statement is said to be unreachable.

示例中In the example

void F() {
    Console.WriteLine("reachable");
    goto Label;
    Console.WriteLine("unreachable");
    Label:
    Console.WriteLine("reachable");
}

无法访问 Console.WriteLine 的第二个调用,因为不可能执行该语句。the second invocation of Console.WriteLine is unreachable because there is no possibility that the statement will be executed.

如果编译器确定无法访问某个语句,则会报告警告。A warning is reported if the compiler determines that a statement is unreachable. 它特别不是错误,无法访问语句。It is specifically not an error for a statement to be unreachable.

若要确定特定的语句或终结点是否可访问,编译器将根据为每个语句定义的可访问性规则执行流分析。To determine whether a particular statement or end point is reachable, the compiler performs flow analysis according to the reachability rules defined for each statement. 流分析考虑了控制语句行为的常量表达式(常量表达式)的值,但不考虑非常量表达式的可能值。The flow analysis takes into account the values of constant expressions (Constant expressions) that control the behavior of statements, but the possible values of non-constant expressions are not considered. 换句话说,出于控制流分析的目的,给定类型的非常量表达式被视为具有该类型的任何可能值。In other words, for purposes of control flow analysis, a non-constant expression of a given type is considered to have any possible value of that type.

示例中In the example

void F() {
    const int i = 1;
    if (i == 2) Console.WriteLine("unreachable");
}

if 语句的布尔表达式是常量表达式,因为 == 运算符的两个操作数都是常量。the boolean expression of the if statement is a constant expression because both operands of the == operator are constants. 在编译时计算常量表达式,并 false生成值时,Console.WriteLine 调用被视为不可访问。As the constant expression is evaluated at compile-time, producing the value false, the Console.WriteLine invocation is considered unreachable. 但是,如果 i 更改为本地变量However, if i is changed to be a local variable

void F() {
    int i = 1;
    if (i == 2) Console.WriteLine("reachable");
}

Console.WriteLine 调用被视为可访问,但实际上,它将永远不会执行。the Console.WriteLine invocation is considered reachable, even though, in reality, it will never be executed.

函数成员的始终被认为是可访问的。The block of a function member is always considered reachable. 通过依次计算块中每个语句的可访问性规则,可以确定任何给定语句的可访问性。By successively evaluating the reachability rules of each statement in a block, the reachability of any given statement can be determined.

示例中In the example

void F(int x) {
    Console.WriteLine("start");
    if (x < 0) Console.WriteLine("negative");
}

确定第二个 Console.WriteLine 的可访问性,如下所示:the reachability of the second Console.WriteLine is determined as follows:

  • 第一个 Console.WriteLine expression 语句是可访问的,因为 F 方法的块是可访问的。The first Console.WriteLine expression statement is reachable because the block of the F method is reachable.
  • 第一个 Console.WriteLine expression 语句的结束点是可访问的,因为该语句是可访问的。The end point of the first Console.WriteLine expression statement is reachable because that statement is reachable.
  • 由于第一个 Console.WriteLine 表达式语句的结束点是可访问的,因此可以访问 if 语句。The if statement is reachable because the end point of the first Console.WriteLine expression statement is reachable.
  • 第二个 Console.WriteLine expression 语句是可访问的,因为 if 语句的布尔表达式不具有 false的常量值。The second Console.WriteLine expression statement is reachable because the boolean expression of the if statement does not have the constant value false.

在以下两种情况下,可能会发生编译时错误,导致语句的终结点可到达:There are two situations in which it is a compile-time error for the end point of a statement to be reachable:

  • 由于 switch 语句不允许 switch 节 "贯穿" 到下一个开关部分,因此在开关部分的语句列表的终结点可访问时,会发生编译时错误。Because the switch statement does not permit a switch section to "fall through" to the next switch section, it is a compile-time error for the end point of the statement list of a switch section to be reachable. 如果发生此错误,则通常指示缺少 break 语句。If this error occurs, it is typically an indication that a break statement is missing.
  • 对于计算要访问的值的函数成员块,它是一个编译时错误。It is a compile-time error for the end point of the block of a function member that computes a value to be reachable. 如果发生此错误,则通常指示缺少 return 语句。If this error occurs, it typically is an indication that a return statement is missing.

BlocksBlocks

使用代码块,可以在允许编写一个语句的上下文中编写多个语句。A block permits multiple statements to be written in contexts where a single statement is allowed.

block
    : '{' statement_list? '}'
    ;

由括在大括号中的可选statement_list语句列表)组成。A block consists of an optional statement_list (Statement lists), enclosed in braces. 如果省略了语句列表,则称块为空。If the statement list is omitted, the block is said to be empty.

块可以包含声明语句(声明语句)。A block may contain declaration statements (Declaration statements). 块中声明的局部变量或常量的范围为块。The scope of a local variable or constant declared in a block is the block.

块的执行方式如下:A block is executed as follows:

  • 如果块为空,控制将被传输到块的终结点。If the block is empty, control is transferred to the end point of the block.
  • 如果块不为空,则将控制转移到语句列表。If the block is not empty, control is transferred to the statement list. 当和如果控件到达语句列表的终点时,控制将被传输到块的终结点。When and if control reaches the end point of the statement list, control is transferred to the end point of the block.

如果块本身是可访问的,则块的语句列表是可访问的。The statement list of a block is reachable if the block itself is reachable.

如果块为空或语句列表的终结点是可访问的,则块的终结点是可访问的。The end point of a block is reachable if the block is empty or if the end point of the statement list is reachable.

包含一个或多个 yield 语句(yield 语句)的称为迭代器块。A block that contains one or more yield statements (The yield statement) is called an iterator block. 迭代器块用于将函数成员作为迭代器(迭代器)实现。Iterator blocks are used to implement function members as iterators (Iterators). 迭代器块适用于一些附加限制:Some additional restrictions apply to iterator blocks:

  • return 语句出现在迭代器块中(但允许 yield return 语句),这是编译时错误。It is a compile-time error for a return statement to appear in an iterator block (but yield return statements are permitted).
  • 迭代器块包含不安全的上下文(不安全上下文)是编译时错误。It is a compile-time error for an iterator block to contain an unsafe context (Unsafe contexts). 迭代器块始终定义安全上下文,即使其声明嵌套在不安全的上下文中也是如此。An iterator block always defines a safe context, even when its declaration is nested in an unsafe context.

语句列表Statement lists

语句列表包含一个或多个按顺序编写的语句。A statement list consists of one or more statements written in sequence. 语句列表在s ()中出现,在switch_blocks 中(switch 语句)。Statement lists occur in blocks (Blocks) and in switch_blocks (The switch statement).

statement_list
    : statement+
    ;

语句列表通过将控制转移到第一条语句来执行。A statement list is executed by transferring control to the first statement. 当和如果控件到达语句的结束点时,控制将转移到下一个语句。When and if control reaches the end point of a statement, control is transferred to the next statement. 当和如果控件到达最后一个语句的终点时,控件将被传输到语句列表的终结点。When and if control reaches the end point of the last statement, control is transferred to the end point of the statement list.

如果以下至少一个条件为 true,则语句列表中的语句是可访问的:A statement in a statement list is reachable if at least one of the following is true:

  • 语句是第一条语句,语句列表本身是可访问的。The statement is the first statement and the statement list itself is reachable.
  • 可以访问前面语句的终点。The end point of the preceding statement is reachable.
  • 语句是标记的语句,并且标签由可访问的 goto 语句引用。The statement is a labeled statement and the label is referenced by a reachable goto statement.

如果列表中最后一个语句的结束点是可访问的,则该语句列表的终结点是可访问的。The end point of a statement list is reachable if the end point of the last statement in the list is reachable.

空语句The empty statement

Empty_statement不执行任何操作。An empty_statement does nothing.

empty_statement
    : ';'
    ;

在需要语句的上下文中没有要执行的操作时,将使用空语句。An empty statement is used when there are no operations to perform in a context where a statement is required.

空语句的执行只是将控制转移到语句的终结点。Execution of an empty statement simply transfers control to the end point of the statement. 因此,如果可以访问空语句,则可以访问空语句的结束点。Thus, the end point of an empty statement is reachable if the empty statement is reachable.

使用 null 体写入 while 语句时,可以使用空语句:An empty statement can be used when writing a while statement with a null body:

bool ProcessMessage() {...}

void ProcessMessages() {
    while (ProcessMessage())
        ;
}

此外,空语句还可用于在块的关闭 "}" 之前声明标签:Also, an empty statement can be used to declare a label just before the closing "}" of a block:

void F() {
    ...
    if (done) goto exit;
    ...
    exit: ;
}

带标签的语句Labeled statements

Labeled_statement允许语句以标签为前缀。A labeled_statement permits a statement to be prefixed by a label. 标记语句允许出现在块中,但不允许作为嵌入语句使用。Labeled statements are permitted in blocks, but are not permitted as embedded statements.

labeled_statement
    : identifier ':' statement
    ;

标记语句使用标识符给定的名称声明标签。A labeled statement declares a label with the name given by the identifier. 标签的作用域是在其中声明标签的整个块,包括任何嵌套块。The scope of a label is the whole block in which the label is declared, including any nested blocks. 对于具有相同名称的两个同名标签,它是编译时错误。It is a compile-time error for two labels with the same name to have overlapping scopes.

可以从标签范围内的 goto 语句(goto 语句)引用标签。A label can be referenced from goto statements (The goto statement) within the scope of the label. 这意味着 goto 语句可以将控制转移到块中,而不是块中的块。This means that goto statements can transfer control within blocks and out of blocks, but never into blocks.

标签具有自己的声明空间,不会干扰其他标识符。Labels have their own declaration space and do not interfere with other identifiers. 示例The example

int F(int x) {
    if (x >= 0) goto x;
    x = -x;
    x: return x;
}

有效,并使用名称 x 同时作为参数和标签。is valid and uses the name x as both a parameter and a label.

标记语句的执行与标签后面的语句的执行完全对应。Execution of a labeled statement corresponds exactly to execution of the statement following the label.

除了正常控制流提供的可访问性外,如果标签由可访问的 goto 语句引用,则可以访问标记的语句。In addition to the reachability provided by normal flow of control, a labeled statement is reachable if the label is referenced by a reachable goto statement. (异常:如果 goto 语句位于包含 finally 块的 try 中,且标记语句位于 try外,并且无法访问 finally 块的终结点,则无法从该 goto 语句访问标记的语句。)(Exception: If a goto statement is inside a try that includes a finally block, and the labeled statement is outside the try, and the end point of the finally block is unreachable, then the labeled statement is not reachable from that goto statement.)

声明语句Declaration statements

声明局部变量或常量的declaration_statementA declaration_statement declares a local variable or constant. 声明语句在块中是允许的,但不允许作为嵌入语句。Declaration statements are permitted in blocks, but are not permitted as embedded statements.

declaration_statement
    : local_variable_declaration ';'
    | local_constant_declaration ';'
    ;

局部变量声明Local variable declarations

Local_variable_declaration声明一个或多个局部变量。A local_variable_declaration declares one or more local variables.

local_variable_declaration
    : local_variable_type local_variable_declarators
    ;

local_variable_type
    : type
    | 'var'
    ;

local_variable_declarators
    : local_variable_declarator
    | local_variable_declarators ',' local_variable_declarator
    ;

local_variable_declarator
    : identifier
    | identifier '=' local_variable_initializer
    ;

local_variable_initializer
    : expression
    | array_initializer
    | local_variable_initializer_unsafe
    ;

Local_variable_declarationlocal_variable_type直接指定声明引入的变量类型,或者使用标识符 var 指示应基于初始值设定项推断类型。The local_variable_type of a local_variable_declaration either directly specifies the type of the variables introduced by the declaration, or indicates with the identifier var that the type should be inferred based on an initializer. 该类型后跟一个local_variable_declarators 列表,其中每个都引入一个新变量。The type is followed by a list of local_variable_declarators, each of which introduces a new variable. Local_variable_declarator包含命名变量的标识符,可选择后跟 "=" 标记和提供变量初始值的local_variable_initializerA local_variable_declarator consists of an identifier that names the variable, optionally followed by an "=" token and a local_variable_initializer that gives the initial value of the variable.

在局部变量声明的上下文中,标识符 var 用作上下文关键字(关键字)。如果将local_variable_type指定为 var 并且未在范围中指定名为 var 的类型,则声明为隐式类型化局部变量声明,其类型是从关联的初始值设定项表达式的类型推断而来的。In the context of a local variable declaration, the identifier var acts as a contextual keyword (Keywords).When the local_variable_type is specified as var and no type named var is in scope, the declaration is an implicitly typed local variable declaration, whose type is inferred from the type of the associated initializer expression. 隐式类型的局部变量声明受到以下限制:Implicitly typed local variable declarations are subject to the following restrictions:

  • Local_variable_declaration不能包含多个local_variable_declaratorThe local_variable_declaration cannot include multiple local_variable_declarators.
  • Local_variable_declarator必须包含local_variable_initializerThe local_variable_declarator must include a local_variable_initializer.
  • Local_variable_initializer必须是表达式The local_variable_initializer must be an expression.
  • 初始值设定项表达式必须具有编译时类型。The initializer expression must have a compile-time type.
  • 初始值设定项表达式不能引用声明的变量本身The initializer expression cannot refer to the declared variable itself

下面是错误的隐式类型化局部变量声明的示例:The following are examples of incorrect implicitly typed local variable declarations:

var x;               // Error, no initializer to infer type from
var y = {1, 2, 3};   // Error, array initializer not permitted
var z = null;        // Error, null does not have a type
var u = x => x + 1;  // Error, anonymous functions do not have a type
var v = v++;         // Error, initializer cannot refer to variable itself

局部变量的值是使用simple_name简单名称)在表达式中获取的,使用赋值赋值运算符)修改本地变量的值。The value of a local variable is obtained in an expression using a simple_name (Simple names), and the value of a local variable is modified using an assignment (Assignment operators). 局部变量在获取其值的每个位置都必须明确赋值(明确赋值)。A local variable must be definitely assigned (Definite assignment) at each location where its value is obtained.

local_variable_declaration中声明的局部变量的作用域是在其中进行声明的块。The scope of a local variable declared in a local_variable_declaration is the block in which the declaration occurs. 在本地变量local_variable_declarator之前的文本位置引用局部变量是错误的。It is an error to refer to a local variable in a textual position that precedes the local_variable_declarator of the local variable. 在局部变量的作用域内,使用相同的名称声明另一个局部变量或常量时,会发生编译时错误。Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.

声明多个变量的局部变量声明等效于多个具有相同类型的单个变量的声明。A local variable declaration that declares multiple variables is equivalent to multiple declarations of single variables with the same type. 此外,局部变量声明中的变量初始值设定项完全对应于紧接在声明后插入的赋值语句。Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration.

示例The example

void F() {
    int x = 1, y, z = x * 2;
}

完全对应于corresponds exactly to

void F() {
    int x; x = 1;
    int y;
    int z; z = x * 2;
}

在隐式类型的局部变量声明中,声明的局部变量的类型将被视为与用于初始化变量的表达式的类型相同。In an implicitly typed local variable declaration, the type of the local variable being declared is taken to be the same as the type of the expression used to initialize the variable. 例如:For example:

var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();

上面的隐式类型局部变量声明完全等效于以下显式类型化声明:The implicitly typed local variable declarations above are precisely equivalent to the following explicitly typed declarations:

int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();

局部常量声明Local constant declarations

一个local_constant_declaration声明一个或多个本地常量。A local_constant_declaration declares one or more local constants.

local_constant_declaration
    : 'const' type constant_declarators
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Local_constant_declaration类型指定声明引入的常量的类型。The type of a local_constant_declaration specifies the type of the constants introduced by the declaration. 该类型后跟一个constant_declarators 列表,其中每个都引入一个新常量。The type is followed by a list of constant_declarators, each of which introduces a new constant. Constant_declarator包含一个标识符,该标识符对常量进行命名,后跟一个 "=" 标记,后跟一个提供常量值的constant_expression常数表达式)。A constant_declarator consists of an identifier that names the constant, followed by an "=" token, followed by a constant_expression (Constant expressions) that gives the value of the constant.

局部常量声明的类型constant_expression必须遵循与常量成员声明(常量)相同的规则。The type and constant_expression of a local constant declaration must follow the same rules as those of a constant member declaration (Constants).

局部变量的值是使用simple_name简单名称)在表达式中获取的。The value of a local constant is obtained in an expression using a simple_name (Simple names).

局部常数的作用域是在其中进行声明的块。The scope of a local constant is the block in which the declaration occurs. constant_declarator之前的文本位置引用本地常量是错误的。It is an error to refer to a local constant in a textual position that precedes its constant_declarator. 在局部常数的范围内,使用相同的名称声明另一个局部变量或常量时,会发生编译时错误。Within the scope of a local constant, it is a compile-time error to declare another local variable or constant with the same name.

声明多个常量的局部常量声明等效于多个具有相同类型的单个常量的声明。A local constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same type.

表达式语句Expression statements

Expression_statement计算给定表达式的值。An expression_statement evaluates a given expression. 由表达式计算的值(如果有)将被丢弃。The value computed by the expression, if any, is discarded.

expression_statement
    : statement_expression ';'
    ;

statement_expression
    : invocation_expression
    | null_conditional_invocation_expression
    | object_creation_expression
    | assignment
    | post_increment_expression
    | post_decrement_expression
    | pre_increment_expression
    | pre_decrement_expression
    | await_expression
    ;

并非所有表达式都允许作为语句。Not all expressions are permitted as statements. 具体而言,不允许使用只计算值(将被丢弃)的表达式(如 x + yx == 1)作为语句。In particular, expressions such as x + y and x == 1 that merely compute a value (which will be discarded), are not permitted as statements.

执行expression_statement将计算包含的表达式,然后将控制转移到expression_statement的终结点。Execution of an expression_statement evaluates the contained expression and then transfers control to the end point of the expression_statement. 如果expression_statement可访问,则可到达expression_statement的终结点。The end point of an expression_statement is reachable if that expression_statement is reachable.

选择语句Selection statements

选择语句根据某个表达式的值为执行选择多个可能的语句之一。Selection statements select one of a number of possible statements for execution based on the value of some expression.

selection_statement
    : if_statement
    | switch_statement
    ;

If 语句The if statement

if 语句基于布尔表达式的值来选择要执行的语句。The if statement selects a statement for execution based on the value of a boolean expression.

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement 'else' embedded_statement
    ;

else 部分与语法允许的上一个词法上最近的 if 相关联。An else part is associated with the lexically nearest preceding if that is allowed by the syntax. 因此,形式的 if 语句Thus, an if statement of the form

if (x) if (y) F(); else G();

等效于is equivalent to

if (x) {
    if (y) {
        F();
    }
    else {
        G();
    }
}

执行 if 语句,如下所示:An if statement is executed as follows:

  • 计算boolean_expression布尔表达式)。The boolean_expression (Boolean expressions) is evaluated.
  • 如果布尔表达式产生 true,则将控制转移到第一个嵌入语句。If the boolean expression yields true, control is transferred to the first embedded statement. 当和如果控件到达该语句的终点时,控制将被传输到 if 语句的终点。When and if control reaches the end point of that statement, control is transferred to the end point of the if statement.
  • 如果布尔表达式产生 false 并且 else 部分存在,则将控制权转移到第二个嵌入语句。If the boolean expression yields false and if an else part is present, control is transferred to the second embedded statement. 当和如果控件到达该语句的终点时,控制将被传输到 if 语句的终点。When and if control reaches the end point of that statement, control is transferred to the end point of the if statement.
  • 如果布尔表达式产生 false 并且 else 部分不存在,则将控制权转移到 if 语句的终点。If the boolean expression yields false and if an else part is not present, control is transferred to the end point of the if statement.

如果 if 语句可访问且布尔表达式不具有 false的常量值,则可以访问 if 语句的第一个嵌入语句。The first embedded statement of an if statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false.

如果 if 语句的第二个嵌入语句(如果存在)可访问,如果 if 语句可访问且布尔表达式不具有 true的常量值。The second embedded statement of an if statement, if present, is reachable if the if statement is reachable and the boolean expression does not have the constant value true.

如果可以访问一个或多个嵌入语句的结束点,则可到达 if 语句的终点。The end point of an if statement is reachable if the end point of at least one of its embedded statements is reachable. 此外,如果 if 语句是可访问的,并且布尔表达式没有 true的常量值,则可访问没有 else 部分的 if 语句的结束点。In addition, the end point of an if statement with no else part is reachable if the if statement is reachable and the boolean expression does not have the constant value true.

Switch 语句The switch statement

Switch 语句为执行选择一个语句列表,其中包含与 switch 表达式的值相对应的关联 switch 标签。The switch statement selects for execution a statement list having an associated switch label that corresponds to the value of the switch expression.

switch_statement
    : 'switch' '(' expression ')' switch_block
    ;

switch_block
    : '{' switch_section* '}'
    ;

switch_section
    : switch_label+ statement_list
    ;

switch_label
    : 'case' constant_expression ':'
    | 'default' ':'
    ;

Switch_statement包含关键字 switch,后跟带括号的表达式(称为 switch 表达式),后跟一个switch_blockA switch_statement consists of the keyword switch, followed by a parenthesized expression (called the switch expression), followed by a switch_block. Switch_block由零个或多个switch_section组成,括在大括号中。The switch_block consists of zero or more switch_sections, enclosed in braces. 每个switch_section都包含一个或多个switch_label,后跟一个statement_list语句列表)。Each switch_section consists of one or more switch_labels followed by a statement_list (Statement lists).

switch 语句的管理类型由 switch 表达式建立。The governing type of a switch statement is established by the switch expression.

  • 如果 switch 表达式的类型为 sbytebyteshortushortintuintlongulongboolcharstringenum_type,或者它是对应于其中一个类型的可以为 null 的类型,则这是 switch 语句的管理类型。If the type of the switch expression is sbyte, byte, short, ushort, int, uint, long, ulong, bool, char, string, or an enum_type, or if it is the nullable type corresponding to one of these types, then that is the governing type of the switch statement.
  • 否则,必须有一个用户定义的隐式转换(用户定义的转换)从 switch 表达式的类型到以下可能的管辖类型之一: sbytebyteshortushortintuintlongulongcharstring或,这是与这些类型之一对应的可以为 null 的类型。Otherwise, exactly one user-defined implicit conversion (User-defined conversions) must exist from the type of the switch expression to one of the following possible governing types: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type corresponding to one of those types.
  • 否则,如果不存在这样的隐式转换,或者存在多个这样的隐式转换,则会发生编译时错误。Otherwise, if no such implicit conversion exists, or if more than one such implicit conversion exists, a compile-time error occurs.

每个 case 标签的常量表达式必须表示一个可隐式转换为switch 语句的管理类型的值。The constant expression of each case label must denote a value that is implicitly convertible (Implicit conversions) to the governing type of the switch statement. 如果同一 switch 语句中的两个或多个 case 标签指定相同的常量值,则会发生编译时错误。A compile-time error occurs if two or more case labels in the same switch statement specify the same constant value.

Switch 语句中最多只能有一个 default 标签。There can be at most one default label in a switch statement.

执行 switch 语句,如下所示:A switch statement is executed as follows:

  • 将计算 switch 表达式并将其转换为管理类型。The switch expression is evaluated and converted to the governing type.
  • 如果在同一 switch 语句的 case 标签中指定的其中一个常量等于 switch 表达式的值,则会将控制转移到匹配的 case 标签之后的语句列表中。If one of the constants specified in a case label in the same switch statement is equal to the value of the switch expression, control is transferred to the statement list following the matched case label.
  • 如果在同一 switch 语句的 case 标签中指定的常量均不等于 switch 表达式的值,并且如果存在 default 标签,则会将控制转移到后面的 default 标签后面的语句列表中。If none of the constants specified in case labels in the same switch statement is equal to the value of the switch expression, and if a default label is present, control is transferred to the statement list following the default label.
  • 如果在同一 switch 语句的 case 标签中指定的常量均不等于 switch 表达式的值,并且如果不存在 default 标签,则会将控制转移到 switch 语句的终点。If none of the constants specified in case labels in the same switch statement is equal to the value of the switch expression, and if no default label is present, control is transferred to the end point of the switch statement.

如果开关部分的语句列表的结束点是可访问的,则会发生编译时错误。If the end point of the statement list of a switch section is reachable, a compile-time error occurs. 这称为 "不贯穿" 规则。This is known as the "no fall through" rule. 示例The example

switch (i) {
case 0:
    CaseZero();
    break;
case 1:
    CaseOne();
    break;
default:
    CaseOthers();
    break;
}

有效,因为没有开关部分具有可访问的终结点。is valid because no switch section has a reachable end point. 与 C 和C++不同,switch 节的执行不允许 "贯穿" 到下一个开关部分,示例Unlike C and C++, execution of a switch section is not permitted to "fall through" to the next switch section, and the example

switch (i) {
case 0:
    CaseZero();
case 1:
    CaseZeroOrOne();
default:
    CaseAny();
}

导致编译时错误。results in a compile-time error. 当执行 switch 节后,若要执行另一个 switch 节,必须使用显式 goto casegoto default 语句:When execution of a switch section is to be followed by execution of another switch section, an explicit goto case or goto default statement must be used:

switch (i) {
case 0:
    CaseZero();
    goto case 1;
case 1:
    CaseZeroOrOne();
    goto default;
default:
    CaseAny();
    break;
}

Switch_section允许使用多个标签。Multiple labels are permitted in a switch_section. 示例The example

switch (i) {
case 0:
    CaseZero();
    break;
case 1:
    CaseOne();
    break;
case 2:
default:
    CaseTwo();
    break;
}

有效。is valid. 该示例不违反 "no 贯穿" 规则,因为标签 case 2:default: 属于同一switch_sectionThe example does not violate the "no fall through" rule because the labels case 2: and default: are part of the same switch_section.

"不贯穿" 规则可防止 C 中发生的常见错误类,以及C++在意外省略 break 语句时。The "no fall through" rule prevents a common class of bugs that occur in C and C++ when break statements are accidentally omitted. 此外,由于此规则,可以任意重新排列 switch 语句的 switch 部分,而不会影响语句的行为。In addition, because of this rule, the switch sections of a switch statement can be arbitrarily rearranged without affecting the behavior of the statement. 例如,上面的 switch 语句的各个部分可以反向,而不会影响语句的行为:For example, the sections of the switch statement above can be reversed without affecting the behavior of the statement:

switch (i) {
default:
    CaseAny();
    break;
case 1:
    CaseZeroOrOne();
    goto default;
case 0:
    CaseZero();
    goto case 1;
}

开关部分的语句列表通常以 breakgoto casegoto default 语句结束,但允许不能使用呈现语句列表的终结点的任何构造。The statement list of a switch section typically ends in a break, goto case, or goto default statement, but any construct that renders the end point of the statement list unreachable is permitted. 例如,已知由布尔表达式 true 控制的 while 语句永远不会到达其终结点。For example, a while statement controlled by the boolean expression true is known to never reach its end point. 同样,throwreturn 语句始终会将控制转移到其他位置,而永远不会到达终结点。Likewise, a throw or return statement always transfers control elsewhere and never reaches its end point. 因此,下面的示例是有效的:Thus, the following example is valid:

switch (i) {
case 0:
    while (true) F();
case 1:
    throw new ArgumentException();
case 2:
    return;
}

switch 语句的管理类型可能是 string类型。The governing type of a switch statement may be the type string. 例如:For example:

void DoCommand(string command) {
    switch (command.ToLower()) {
    case "run":
        DoRun();
        break;
    case "save":
        DoSave();
        break;
    case "quit":
        DoQuit();
        break;
    default:
        InvalidCommand(command);
        break;
    }
}

与字符串相等运算符(string 相等运算符)一样,switch 语句区分大小写,并且仅当 switch 表达式字符串与 case 标签常量完全匹配时,才会执行给定的 switch 节。Like the string equality operators (String equality operators), the switch statement is case sensitive and will execute a given switch section only if the switch expression string exactly matches a case label constant.

string``switch 语句的管理类型时,允许将值 null 为 case 标签常量。When the governing type of a switch statement is string, the value null is permitted as a case label constant.

Switch_blockstatement_list可能包含声明语句(声明语句)。The statement_lists of a switch_block may contain declaration statements (Declaration statements). Switch 块中声明的局部变量或常量的范围为 switch 块。The scope of a local variable or constant declared in a switch block is the switch block.

如果 switch 语句可访问,且至少满足以下条件之一,则给定 switch 节的语句列表是可访问的:The statement list of a given switch section is reachable if the switch statement is reachable and at least one of the following is true:

  • Switch 表达式是一个非常量的值。The switch expression is a non-constant value.
  • Switch 表达式是一个与开关部分中的 case 标签匹配的常量值。The switch expression is a constant value that matches a case label in the switch section.
  • Switch 表达式是不与任何 case 标签匹配的常量值,并且开关部分包含 default 标签。The switch expression is a constant value that doesn't match any case label, and the switch section contains the default label.
  • 开关部分的开关标签由可访问的 goto casegoto default 语句引用。A switch label of the switch section is referenced by a reachable goto case or goto default statement.

如果以下至少一个条件为 true,则可到达 switch 语句的终结点:The end point of a switch statement is reachable if at least one of the following is true:

  • switch 语句包含可访问的 break 语句,该语句可退出 switch 语句。The switch statement contains a reachable break statement that exits the switch statement.
  • switch 语句是可访问的,则 switch 表达式为非常量值,并且不存在任何 default 标签。The switch statement is reachable, the switch expression is a non-constant value, and no default label is present.
  • switch 语句是可访问的,则 switch 表达式是一个与任何 case 标签都不匹配的常量值,并且不存在 default 标签。The switch statement is reachable, the switch expression is a constant value that doesn't match any case label, and no default label is present.

迭代语句Iteration statements

迭代语句重复执行嵌入语句。Iteration statements repeatedly execute an embedded statement.

iteration_statement
    : while_statement
    | do_statement
    | for_statement
    | foreach_statement
    ;

While 语句The while statement

while 语句有条件地执行一个嵌入语句零次或多次。The while statement conditionally executes an embedded statement zero or more times.

while_statement
    : 'while' '(' boolean_expression ')' embedded_statement
    ;

执行 while 语句,如下所示:A while statement is executed as follows:

  • 计算boolean_expression布尔表达式)。The boolean_expression (Boolean expressions) is evaluated.
  • 如果布尔表达式产生 true,则将控制转移到嵌入语句。If the boolean expression yields true, control is transferred to the embedded statement. 当和如果控件到达嵌入语句的结束点(可能是执行 continue 语句)时,控制将转移到 while 语句的开头。When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), control is transferred to the beginning of the while statement.
  • 如果布尔表达式产生 false,则将控制转移到 while 语句的终点。If the boolean expression yields false, control is transferred to the end point of the while statement.

while 语句的嵌入语句中,break 语句(break 语句)可用于将控制转移到 while 语句的终结点(从而结束嵌入语句的迭代),并且 continue 语句(continue 语句)可用于将控制转移到嵌入语句的终结点(从而执行 while 语句的另一次迭代)。Within the embedded statement of a while statement, a break statement (The break statement) may be used to transfer control to the end point of the while statement (thus ending iteration of the embedded statement), and a continue statement (The continue statement) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the while statement).

如果 while 语句可访问且布尔表达式不具有 false的常量值,则可以访问 while 语句的嵌入语句。The embedded statement of a while statement is reachable if the while statement is reachable and the boolean expression does not have the constant value false.

如果以下至少一个条件为 true,则可到达 while 语句的终结点:The end point of a while statement is reachable if at least one of the following is true:

  • while 语句包含可访问的 break 语句,该语句可退出 while 语句。The while statement contains a reachable break statement that exits the while statement.
  • while 语句是可访问的,并且布尔表达式没有 true的常量值。The while statement is reachable and the boolean expression does not have the constant value true.

Do 语句The do statement

do 语句有条件地执行一次或多次嵌入式语句。The do statement conditionally executes an embedded statement one or more times.

do_statement
    : 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
    ;

执行 do 语句,如下所示:A do statement is executed as follows:

  • 控件将被传输到嵌入语句。Control is transferred to the embedded statement.
  • 当和如果控件到达嵌入语句的终结点(可能是执行 continue 语句时),将计算boolean_expression布尔表达式)。When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the boolean_expression (Boolean expressions) is evaluated. 如果布尔表达式产生 true,则将控制转移到 do 语句的开头。If the boolean expression yields true, control is transferred to the beginning of the do statement. 否则,控制将转移到 do 语句的终点。Otherwise, control is transferred to the end point of the do statement.

do 语句的嵌入语句中,break 语句(break 语句)可用于将控制转移到 do 语句的终结点(从而结束嵌入语句的迭代),而 continue 语句(continue 语句)可用于将控制转移到嵌入语句的终结点。Within the embedded statement of a do statement, a break statement (The break statement) may be used to transfer control to the end point of the do statement (thus ending iteration of the embedded statement), and a continue statement (The continue statement) may be used to transfer control to the end point of the embedded statement.

如果 do 语句可访问,则可以访问 do 语句的嵌入语句。The embedded statement of a do statement is reachable if the do statement is reachable.

如果以下至少一个条件为 true,则可到达 do 语句的终结点:The end point of a do statement is reachable if at least one of the following is true:

  • do 语句包含可访问的 break 语句,该语句可退出 do 语句。The do statement contains a reachable break statement that exits the do statement.
  • 嵌入语句的结束点是可访问的,并且布尔表达式没有 true的常量值。The end point of the embedded statement is reachable and the boolean expression does not have the constant value true.

For 语句The for statement

for 语句计算一系列初始化表达式,然后,在条件为 true 时,重复执行嵌入语句并计算迭代表达式的序列。The for statement evaluates a sequence of initialization expressions and then, while a condition is true, repeatedly executes an embedded statement and evaluates a sequence of iteration expressions.

for_statement
    : 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')' embedded_statement
    ;

for_initializer
    : local_variable_declaration
    | statement_expression_list
    ;

for_condition
    : boolean_expression
    ;

for_iterator
    : statement_expression_list
    ;

statement_expression_list
    : statement_expression (',' statement_expression)*
    ;

For_initializer(如果存在)由local_variable_declaration局部变量声明)或以逗号分隔的statement_expressions (expression 语句)的列表组成。The for_initializer, if present, consists of either a local_variable_declaration (Local variable declarations) or a list of statement_expressions (Expression statements) separated by commas. For_initializer声明的局部变量的作用域从变量的local_variable_declarator开始,并延伸到嵌入语句的末尾。The scope of a local variable declared by a for_initializer starts at the local_variable_declarator for the variable and extends to the end of the embedded statement. 范围包括for_conditionfor_iteratorThe scope includes the for_condition and the for_iterator.

For_condition(如果存在)必须是boolean_expression布尔表达式)。The for_condition, if present, must be a boolean_expression (Boolean expressions).

For_iterator(如果存在)由以逗号分隔的Statement_expressions (expression 语句)列表组成。The for_iterator, if present, consists of a list of statement_expressions (Expression statements) separated by commas.

For 语句的执行方式如下:A for statement is executed as follows:

  • 如果for_initializer存在,变量初始值设定项或语句表达式将按照它们的写入顺序执行。If a for_initializer is present, the variable initializers or statement expressions are executed in the order they are written. 此步骤只执行一次。This step is only performed once.
  • 如果for_condition存在,则将对其进行计算。If a for_condition is present, it is evaluated.
  • 如果for_condition不存在或者计算产生 true,则将控制转移到嵌入语句。If the for_condition is not present or if the evaluation yields true, control is transferred to the embedded statement. 当和如果控件到达嵌入语句的结束点(可能是执行 continue 语句)时,将按顺序计算for_iterator的表达式(如果有),然后执行另一次迭代,从对上述步骤中的for_condition进行计算开始。When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the expressions of the for_iterator, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for_condition in the step above.
  • 如果for_condition存在并且计算生成 false,则控制将转移到 for 语句的终结点。If the for_condition is present and the evaluation yields false, control is transferred to the end point of the for statement.

for 语句的嵌入语句内,break 语句(break 语句)可用于将控制转移到 for 语句的终结点(从而结束嵌入语句的迭代),而 continue 语句(continue 语句)可用于将控制转移到嵌入语句的终结点(从而执行for_iterator并执行 for 语句的另一次迭代,从for_condition开始)。Within the embedded statement of a for statement, a break statement (The break statement) may be used to transfer control to the end point of the for statement (thus ending iteration of the embedded statement), and a continue statement (The continue statement) may be used to transfer control to the end point of the embedded statement (thus executing the for_iterator and performing another iteration of the for statement, starting with the for_condition).

如果满足以下条件之一,则可以访问 for 语句的嵌入语句:The embedded statement of a for statement is reachable if one of the following is true:

  • for 语句是可访问的,并且不存在for_conditionThe for statement is reachable and no for_condition is present.
  • for 语句是可访问的,并且for_condition存在并且没有 false的常量值。The for statement is reachable and a for_condition is present and does not have the constant value false.

如果以下至少一个条件为 true,则可到达 for 语句的终结点:The end point of a for statement is reachable if at least one of the following is true:

  • for 语句包含可访问的 break 语句,该语句可退出 for 语句。The for statement contains a reachable break statement that exits the for statement.
  • for 语句是可访问的,并且for_condition存在并且没有 true的常量值。The for statement is reachable and a for_condition is present and does not have the constant value true.

Foreach 语句The foreach statement

foreach 语句枚举集合中的元素,并对集合中的每个元素执行嵌入语句。The foreach statement enumerates the elements of a collection, executing an embedded statement for each element of the collection.

foreach_statement
    : 'foreach' '(' local_variable_type identifier 'in' expression ')' embedded_statement
    ;

foreach 语句的类型标识符声明语句的迭代变量The type and identifier of a foreach statement declare the iteration variable of the statement. 如果 var 标识符作为local_variable_type提供,且没有任何名为 var 的类型在范围内,则将迭代变量称为隐式类型的迭代变量,并将其类型视为 foreach 语句的元素类型,如下所示。If the var identifier is given as the local_variable_type, and no type named var is in scope, the iteration variable is said to be an implicitly typed iteration variable, and its type is taken to be the element type of the foreach statement, as specified below. 迭代变量对应于一个只读局部变量,该局部变量具有扩展到嵌入语句的范围。The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement. foreach 语句执行过程中,迭代变量表示当前正在为其执行迭代的集合元素。During execution of a foreach statement, the iteration variable represents the collection element for which an iteration is currently being performed. 如果嵌入的语句尝试修改迭代变量(通过赋值或 ++-- 运算符),或将迭代变量作为 refout 参数传递,则会发生编译时错误。A compile-time error occurs if the embedded statement attempts to modify the iteration variable (via assignment or the ++ and -- operators) or pass the iteration variable as a ref or out parameter.

在以下情况下,为简洁起见,IEnumerableIEnumerator``IEnumerable<T>IEnumerator<T> 引用命名空间中的相应类型 System.CollectionsSystem.Collections.GenericIn the following, for brevity, IEnumerable, IEnumerator, IEnumerable<T> and IEnumerator<T> refer to the corresponding types in the namespaces System.Collections and System.Collections.Generic.

Foreach 语句的编译时处理首先确定表达式的集合类型枚举器类型元素类型The compile-time processing of a foreach statement first determines the collection type, enumerator type and element type of the expression. 这种决定将按如下方式进行:This determination proceeds as follows:

  • 如果表达式的类型 X 为数组类型,则从 XIEnumerable 接口(因为 System.Array 实现此接口)存在隐式引用转换。If the type X of expression is an array type then there is an implicit reference conversion from X to the IEnumerable interface (since System.Array implements this interface). 集合类型IEnumerable 接口,枚举器类型IEnumerator 接口,元素类型是数组类型 X的元素类型。The collection type is the IEnumerable interface, the enumerator type is the IEnumerator interface and the element type is the element type of the array type X.

  • 如果 dynamic X表达式的类型,则将从expressionIEnumerable 接口(隐式动态转换)的隐式转换。If the type X of expression is dynamic then there is an implicit conversion from expression to the IEnumerable interface (Implicit dynamic conversions). 集合类型IEnumerable 接口,枚举器类型IEnumerator 接口。The collection type is the IEnumerable interface and the enumerator type is the IEnumerator interface. 如果将 var 标识符指定为local_variable_typedynamic元素类型,否则将 object元素类型If the var identifier is given as the local_variable_type then the element type is dynamic, otherwise it is object.

  • 否则,请确定类型 X 是否具有适当的 GetEnumerator 方法:Otherwise, determine whether the type X has an appropriate GetEnumerator method:

    • 在类型 X 上执行成员查找,标识符 GetEnumerator,不包含任何类型参数。Perform member lookup on the type X with identifier GetEnumerator and no type arguments. 如果成员查找不会生成匹配项,或者它产生了多义性,或者产生了不是方法组的匹配项,请检查可枚举的接口,如下所述。If the member lookup does not produce a match, or it produces an ambiguity, or produces a match that is not a method group, check for an enumerable interface as described below. 如果成员查找产生了除方法组以外的任何内容,则建议发出警告。It is recommended that a warning be issued if member lookup produces anything except a method group or no match.
    • 使用生成的方法组和空参数列表执行重载决策。Perform overload resolution using the resulting method group and an empty argument list. 如果重载决策导致没有适用的方法、导致歧义或产生单个最佳方法,但该方法是静态的或非公共的,请按如下所述检查可枚举的接口。If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, check for an enumerable interface as described below. 如果重载决策产生了除明确的公共实例方法之外的任何内容,或者没有适用方法,则建议发出警告。It is recommended that a warning be issued if overload resolution produces anything except an unambiguous public instance method or no applicable methods.
    • 如果 GetEnumerator 方法的返回类型 E 不是类、结构或接口类型,则会生成错误,并且不会执行任何其他步骤。If the return type E of the GetEnumerator method is not a class, struct or interface type, an error is produced and no further steps are taken.
    • 成员查找在标识符为 Current 且无类型参数 E 上执行。Member lookup is performed on E with the identifier Current and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除允许读取的公共实例属性之外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a public instance property that permits reading, an error is produced and no further steps are taken.
    • 成员查找在标识符为 MoveNext 且无类型参数 E 上执行。Member lookup is performed on E with the identifier MoveNext and no type arguments. 如果成员查找没有生成任何匹配项,则结果为错误,或结果为除方法组外的任何内容,并不执行任何其他步骤。If the member lookup produces no match, the result is an error, or the result is anything except a method group, an error is produced and no further steps are taken.
    • 使用空参数列表对方法组执行重载决策。Overload resolution is performed on the method group with an empty argument list. 如果重载决策导致没有适用的方法,导致歧义,或者导致单个最佳方法,但该方法是静态的或非公共的,或者它的返回类型不 bool,则会生成错误,并且不会执行任何其他步骤。If overload resolution results in no applicable methods, results in an ambiguity, or results in a single best method but that method is either static or not public, or its return type is not bool, an error is produced and no further steps are taken.
    • 集合类型X枚举器类型E元素类型Current 属性的类型。The collection type is X, the enumerator type is E, and the element type is the type of the Current property.
  • 否则,请检查可枚举的接口:Otherwise, check for an enumerable interface:

    • 如果 Ti 的所有类型都是从 XIEnumerable<Ti>的隐式转换,则有一个唯一类型 TTdynamic,而对于所有其他 Ti 存在从 IEnumerable<T>IEnumerable<Ti>的隐式转换,则该集合类型为接口 IEnumerable<T>枚举器类型为接口 IEnumerator<T>,且元素类型TIf among all the types Ti for which there is an implicit conversion from X to IEnumerable<Ti>, there is a unique type T such that T is not dynamic and for all the other Ti there is an implicit conversion from IEnumerable<T> to IEnumerable<Ti>, then the collection type is the interface IEnumerable<T>, the enumerator type is the interface IEnumerator<T>, and the element type is T.
    • 否则,如果有多个这样的类型 T,则会生成错误,并且不会执行任何其他步骤。Otherwise, if there is more than one such type T, then an error is produced and no further steps are taken.
    • 否则,如果存在从 XSystem.Collections.IEnumerable 接口的隐式转换,则集合类型为此接口,枚举器类型为接口 System.Collections.IEnumerator,并且元素类型object的。Otherwise, if there is an implicit conversion from X to the System.Collections.IEnumerable interface, then the collection type is this interface, the enumerator type is the interface System.Collections.IEnumerator, and the element type is object.
    • 否则,将生成错误,并且不执行任何其他步骤。Otherwise, an error is produced and no further steps are taken.

如果成功,上述步骤会明确生成 C、枚举器类型 E 和元素类型 T的集合类型。The above steps, if successful, unambiguously produce a collection type C, enumerator type E and element type T. 窗体的 foreach 语句A foreach statement of the form

foreach (V v in x) embedded_statement

然后扩展为:is then expanded to:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        while (e.MoveNext()) {
            V v = (V)(T)e.Current;
            embedded_statement
        }
    }
    finally {
        ... // Dispose e
    }
}

变量 ex 或程序的嵌入语句或任何其他源代码都不可见或可访问。The variable e is not visible to or accessible to the expression x or the embedded statement or any other source code of the program. 变量 v 在嵌入语句中是只读的。The variable v is read-only in the embedded statement. 如果没有从 T (元素类型)到 V (foreach 语句中的local_variable_type )的显式转换(显式转换),则会生成错误,并且不会执行任何其他步骤。If there is not an explicit conversion (Explicit conversions) from T (the element type) to V (the local_variable_type in the foreach statement), an error is produced and no further steps are taken. 如果 x 的值 null,则在运行时将引发 System.NullReferenceExceptionIf x has the value null, a System.NullReferenceException is thrown at run-time.

允许实现以不同的方式实现给定的 foreach 语句(例如出于性能原因),前提是该行为与上述扩展一致。An implementation is permitted to implement a given foreach-statement differently, e.g. for performance reasons, as long as the behavior is consistent with the above expansion.

While 循环内 v 的位置对于在embedded_statement中发生的任何匿名函数的捕获是非常重要的。The placement of v inside the while loop is important for how it is captured by any anonymous function occurring in the embedded_statement.

例如:For example:

int[] values = { 7, 9, 13 };
Action f = null;

foreach (var value in values)
{
    if (f == null) f = () => Console.WriteLine("First value: " + value);
}

f();

如果 v 是在 while 循环之外声明的,则它将在所有迭代之间共享,而 for 循环后,其值将是最终值 13,这是 f 的调用的输出。If v was declared outside of the while loop, it would be shared among all iterations, and its value after the for loop would be the final value, 13, which is what the invocation of f would print. 相反,由于每个迭代都有其自己的变量 v,因此,第一次迭代中 f 捕获的变量将继续保存值 7,这就是要打印的值。Instead, because each iteration has its own variable v, the one captured by f in the first iteration will continue to hold the value 7, which is what will be printed. (注意:更早版本C#的声明在 while 循环外 v。)(Note: earlier versions of C# declared v outside of the while loop.)

Finally 块的主体按照以下步骤进行构造:The body of the finally block is constructed according to the following steps:

  • 如果存在从 ESystem.IDisposable 接口的隐式转换,则If there is an implicit conversion from E to the System.IDisposable interface, then

    • 如果 E 是不可以为 null 的值类型,则将 finally 子句扩展为语义等效项:If E is a non-nullable value type then the finally clause is expanded to the semantic equivalent of:

      finally {
          ((System.IDisposable)e).Dispose();
      }
      
    • 否则,finally 子句将扩展到语义等效项:Otherwise the finally clause is expanded to the semantic equivalent of:

      finally {
          if (e != null) ((System.IDisposable)e).Dispose();
      }
      

    除非 E 是值类型,或者是实例化为值类型的类型参数,否则,将 e 转换为 System.IDisposable 将不会导致装箱发生。except that if E is a value type, or a type parameter instantiated to a value type, then the cast of e to System.IDisposable will not cause boxing to occur.

  • 否则,如果 E 是密封类型,则 finally 子句将扩展为空块:Otherwise, if E is a sealed type, the finally clause is expanded to an empty block:

    finally {
    }
    
  • 否则,finally 子句将扩展为:Otherwise, the finally clause is expanded to:

    finally {
        System.IDisposable d = e as System.IDisposable;
        if (d != null) d.Dispose();
    }
    

    局部变量 d 对任何用户代码都不可见或不可访问。The local variable d is not visible to or accessible to any user code. 具体而言,它不会与范围包含 finally 块的任何其他变量发生冲突。In particular, it does not conflict with any other variable whose scope includes the finally block.

foreach 遍历数组元素的顺序如下:对于一维数组元素按递增的索引顺序进行遍历,从索引 0 开始,以 index Length - 1结尾。The order in which foreach traverses the elements of an array, is as follows: For single-dimensional arrays elements are traversed in increasing index order, starting with index 0 and ending with index Length - 1. 对于多维数组,会遍历元素,以便先增加最右侧维度的索引,然后再增加下一个左侧维度,依此类推。For multi-dimensional arrays, elements are traversed such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left.

下面的示例按元素顺序输出二维数组中的每个值:The following example prints out each value in a two-dimensional array, in element order:

using System;

class Test
{
    static void Main() {
        double[,] values = {
            {1.2, 2.3, 3.4, 4.5},
            {5.6, 6.7, 7.8, 8.9}
        };

        foreach (double elementValue in values)
            Console.Write("{0} ", elementValue);

        Console.WriteLine();
    }
}

生成的输出如下所示:The output produced is as follows:

1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

示例中In the example

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers) Console.WriteLine(n);

n 的类型将被推断为 intnumbers的元素类型)。the type of n is inferred to be int, the element type of numbers.

跳转语句Jump statements

跳转语句无条件传输控制。Jump statements unconditionally transfer control.

jump_statement
    : break_statement
    | continue_statement
    | goto_statement
    | return_statement
    | throw_statement
    ;

跳转语句将控制转移到的位置称为跳转语句的目标The location to which a jump statement transfers control is called the target of the jump statement.

如果跳转语句发生在块中,并且该跳转语句的目标在该块外,则可以使用跳转语句退出块。When a jump statement occurs within a block, and the target of that jump statement is outside that block, the jump statement is said to exit the block. 尽管一个跳转语句可以将控制转移到块外,但它永远不能将控制转移到块中。While a jump statement may transfer control out of a block, it can never transfer control into a block.

由于存在干预 try 语句,执行跳转语句非常复杂。Execution of jump statements is complicated by the presence of intervening try statements. 在缺少此类 try 语句的情况下,跳转语句会无条件地将控制从跳转语句转移到其目标。In the absence of such try statements, a jump statement unconditionally transfers control from the jump statement to its target. 如果存在此类干预 try 语句,则执行会更复杂。In the presence of such intervening try statements, execution is more complex. 如果跳转语句退出一个或多个具有关联 finally 块的 try 块,则控件最初会传输到最内层 try 语句的 finally 块。If the jump statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 重复此过程,直到执行了所有干预 try 语句 finally 块。This process is repeated until the finally blocks of all intervening try statements have been executed.

示例中In the example

using System;

class Test
{
    static void Main() {
        while (true) {
            try {
                try {
                    Console.WriteLine("Before break");
                    break;
                }
                finally {
                    Console.WriteLine("Innermost finally block");
                }
            }
            finally {
                Console.WriteLine("Outermost finally block");
            }
        }
        Console.WriteLine("After break");
    }
}

在控件传输到跳转语句的目标之前,将执行与两个 try 语句关联的 finally 块。the finally blocks associated with two try statements are executed before control is transferred to the target of the jump statement.

生成的输出如下所示:The output produced is as follows:

Before break
Innermost finally block
Outermost finally block
After break

Break 语句The break statement

break 语句退出最近的封闭 switchwhiledoforforeach 语句。The break statement exits the nearest enclosing switch, while, do, for, or foreach statement.

break_statement
    : 'break' ';'
    ;

break 语句的目标是最近的封闭 switchwhiledoforforeach 语句的终点。The target of a break statement is the end point of the nearest enclosing switch, while, do, for, or foreach statement. 如果 break 语句未由 switchwhiledoforforeach 语句括起来,则会发生编译时错误。If a break statement is not enclosed by a switch, while, do, for, or foreach statement, a compile-time error occurs.

当多个 switchwhiledoforforeach 语句彼此嵌套时,break 语句仅适用于最内层的语句。When multiple switch, while, do, for, or foreach statements are nested within each other, a break statement applies only to the innermost statement. 若要跨多个嵌套级别传递控制,必须使用 goto 语句(goto 语句)。To transfer control across multiple nesting levels, a goto statement (The goto statement) must be used.

break 语句无法退出 finally 块(try 语句)。A break statement cannot exit a finally block (The try statement). 如果 break 语句发生在 finally 块中,则 break 语句的目标必须在同一 finally 块内;否则,会发生编译时错误。When a break statement occurs within a finally block, the target of the break statement must be within the same finally block; otherwise, a compile-time error occurs.

执行 break 语句,如下所示:A break statement is executed as follows:

  • 如果 break 语句退出一个或多个具有关联 finally 块的 try 块,则控件最初会传输到最内层 try 语句的 finally 块。If the break statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 重复此过程,直到执行了所有干预 try 语句 finally 块。This process is repeated until the finally blocks of all intervening try statements have been executed.
  • 控件传输到 break 语句的目标。Control is transferred to the target of the break statement.

由于 break 语句无条件地将控制转移到其他位置,因此无法访问 break 语句的终点。Because a break statement unconditionally transfers control elsewhere, the end point of a break statement is never reachable.

Continue 语句The continue statement

continue 语句启动最近的封闭 whiledoforforeach 语句的新迭代。The continue statement starts a new iteration of the nearest enclosing while, do, for, or foreach statement.

continue_statement
    : 'continue' ';'
    ;

continue 语句的目标是最近的封闭 whiledoforforeach 语句的嵌入语句的终点。The target of a continue statement is the end point of the embedded statement of the nearest enclosing while, do, for, or foreach statement. 如果 continue 语句未由 whiledoforforeach 语句括起来,则会发生编译时错误。If a continue statement is not enclosed by a while, do, for, or foreach statement, a compile-time error occurs.

当多个 whiledoforforeach 语句彼此嵌套时,continue 语句仅适用于最内层的语句。When multiple while, do, for, or foreach statements are nested within each other, a continue statement applies only to the innermost statement. 若要跨多个嵌套级别传递控制,必须使用 goto 语句(goto 语句)。To transfer control across multiple nesting levels, a goto statement (The goto statement) must be used.

continue 语句无法退出 finally 块(try 语句)。A continue statement cannot exit a finally block (The try statement). 如果 continue 语句发生在 finally 块中,则 continue 语句的目标必须在同一 finally 块内;否则,会发生编译时错误。When a continue statement occurs within a finally block, the target of the continue statement must be within the same finally block; otherwise a compile-time error occurs.

执行 continue 语句,如下所示:A continue statement is executed as follows:

  • 如果 continue 语句退出一个或多个具有关联 finally 块的 try 块,则控件最初会传输到最内层 try 语句的 finally 块。If the continue statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 重复此过程,直到执行了所有干预 try 语句 finally 块。This process is repeated until the finally blocks of all intervening try statements have been executed.
  • 控件传输到 continue 语句的目标。Control is transferred to the target of the continue statement.

由于 continue 语句无条件地将控制转移到其他位置,因此无法访问 continue 语句的终点。Because a continue statement unconditionally transfers control elsewhere, the end point of a continue statement is never reachable.

goto 语句The goto statement

goto 语句将控制转移到由标签标记的语句。The goto statement transfers control to a statement that is marked by a label.

goto_statement
    : 'goto' identifier ';'
    | 'goto' 'case' constant_expression ';'
    | 'goto' 'default' ';'
    ;

goto identifier语句的目标是带有给定标签的标记语句。The target of a goto identifier statement is the labeled statement with the given label. 如果当前函数成员中不存在具有给定名称的标签,或者如果 goto 语句不在标签范围内,则会发生编译时错误。If a label with the given name does not exist in the current function member, or if the goto statement is not within the scope of the label, a compile-time error occurs. 此规则允许使用 goto 语句将控制转移出嵌套作用域,但不允许使用嵌套作用域。This rule permits the use of a goto statement to transfer control out of a nested scope, but not into a nested scope. 示例中In the example

using System;

class Test
{
    static void Main(string[] args) {
        string[,] table = {
            {"Red", "Blue", "Green"},
            {"Monday", "Wednesday", "Friday"}
        };

        foreach (string str in args) {
            int row, colm;
            for (row = 0; row <= 1; ++row)
                for (colm = 0; colm <= 2; ++colm)
                    if (str == table[row,colm])
                         goto done;

            Console.WriteLine("{0} not found", str);
            continue;
    done:
            Console.WriteLine("Found {0} at [{1}][{2}]", str, row, colm);
        }
    }
}

goto 语句用于将控制转移出嵌套作用域。a goto statement is used to transfer control out of a nested scope.

goto case 语句的目标是直接封闭 switch 语句(switch 语句)中的语句列表,其中包含具有给定常数值的 case 标签。The target of a goto case statement is the statement list in the immediately enclosing switch statement (The switch statement), which contains a case label with the given constant value. 如果 goto case 语句未使用 switch 语句括起来,则如果constant_expression不能隐式转换为最近的封闭 switch 语句的管理类型,或者最近的封闭 switch 语句不包含具有给定常数值的 case 标签,则会发生编译时错误。If the goto case statement is not enclosed by a switch statement, if the constant_expression is not implicitly convertible (Implicit conversions) to the governing type of the nearest enclosing switch statement, or if the nearest enclosing switch statement does not contain a case label with the given constant value, a compile-time error occurs.

goto default 语句的目标是直接封闭 switch 语句(switch 语句)中的语句列表,其中包含一个 default 标签。The target of a goto default statement is the statement list in the immediately enclosing switch statement (The switch statement), which contains a default label. 如果 goto default 语句未用 switch 语句括起来,或者最近的封闭 switch 语句不包含 default 标签,则会发生编译时错误。If the goto default statement is not enclosed by a switch statement, or if the nearest enclosing switch statement does not contain a default label, a compile-time error occurs.

goto 语句无法退出 finally 块(try 语句)。A goto statement cannot exit a finally block (The try statement). 如果 goto 语句发生在 finally 块中,则 goto 语句的目标必须在同一 finally 块中,否则将发生编译时错误。When a goto statement occurs within a finally block, the target of the goto statement must be within the same finally block, or otherwise a compile-time error occurs.

执行 goto 语句,如下所示:A goto statement is executed as follows:

  • 如果 goto 语句退出一个或多个具有关联 finally 块的 try 块,则控件最初会传输到最内层 try 语句的 finally 块。If the goto statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 重复此过程,直到执行了所有干预 try 语句 finally 块。This process is repeated until the finally blocks of all intervening try statements have been executed.
  • 控件传输到 goto 语句的目标。Control is transferred to the target of the goto statement.

由于 goto 语句无条件地将控制转移到其他位置,因此无法访问 goto 语句的终点。Because a goto statement unconditionally transfers control elsewhere, the end point of a goto statement is never reachable.

Return 语句The return statement

return 语句将控制权返回到 return 语句显示函数的当前调用方。The return statement returns control to the current caller of the function in which the return statement appears.

return_statement
    : 'return' expression? ';'
    ;

不带表达式的 return 语句只能在不计算值的函数成员中使用,即,具有结果类型(方法主体void的方法、属性或索引器的 set 访问器、事件的 addremove 访问器、实例构造函数、静态构造函数或析构函数。A return statement with no expression can be used only in a function member that does not compute a value, that is, a method with the result type (Method body) void, the set accessor of a property or indexer, the add and remove accessors of an event, an instance constructor, a static constructor, or a destructor.

带有表达式的 return 语句只能在计算值的函数成员中使用,即,具有非 void 结果类型的方法、属性或索引器的 get 访问器或用户定义的运算符。A return statement with an expression can only be used in a function member that computes a value, that is, a method with a non-void result type, the get accessor of a property or indexer, or a user-defined operator. 隐式转换(隐式转换)必须存在于表达式的类型到包含函数成员的返回类型。An implicit conversion (Implicit conversions) must exist from the type of the expression to the return type of the containing function member.

Return 语句还可用于匿名函数表达式(匿名函数表达式)的主体中,并参与确定哪些转换存在这些函数。Return statements can also be used in the body of anonymous function expressions (Anonymous function expressions), and participate in determining which conversions exist for those functions.

return 语句出现在 finally 块中(try 语句),这是编译时错误。It is a compile-time error for a return statement to appear in a finally block (The try statement).

执行 return 语句,如下所示:A return statement is executed as follows:

  • 如果 return 语句指定一个表达式,将计算该表达式,并通过隐式转换将生成的值转换为包含函数的返回类型。If the return statement specifies an expression, the expression is evaluated and the resulting value is converted to the return type of the containing function by an implicit conversion. 转换的结果成为函数生成的结果值。The result of the conversion becomes the result value produced by the function.
  • 如果 return 语句由一个或多个具有关联 finally 块的 trycatch 块括起来,则控件最初会传输到最内层 finally 语句的 try 块。If the return statement is enclosed by one or more try or catch blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 此过程将重复进行,直到执行完所有封闭 try 语句 finally 块。This process is repeated until the finally blocks of all enclosing try statements have been executed.
  • 如果包含函数不是异步函数,则会将控件返回到包含函数的调用方,同时返回结果值(如果有)。If the containing function is not an async function, control is returned to the caller of the containing function along with the result value, if any.
  • 如果包含函数是一个异步函数,则将控制权返回给当前调用方,并在返回任务中记录结果值(如果有),如(枚举器接口)中所述。If the containing function is an async function, control is returned to the current caller, and the result value, if any, is recorded in the return task as described in (Enumerator interfaces).

由于 return 语句无条件地将控制转移到其他位置,因此无法访问 return 语句的终点。Because a return statement unconditionally transfers control elsewhere, the end point of a return statement is never reachable.

Throw 语句The throw statement

throw 语句引发异常。The throw statement throws an exception.

throw_statement
    : 'throw' expression? ';'
    ;

带有表达式的 throw 语句将引发通过计算表达式生成的值。A throw statement with an expression throws the value produced by evaluating the expression. 表达式必须表示从 System.Exception 派生的类类型 System.Exception类类型的值,或者是具有其有效基类的 System.Exception (或子类)的类型参数类型的值。The expression must denote a value of the class type System.Exception, of a class type that derives from System.Exception or of a type parameter type that has System.Exception (or a subclass thereof) as its effective base class. 如果表达式的计算生成 null,则改为引发 System.NullReferenceExceptionIf evaluation of the expression produces null, a System.NullReferenceException is thrown instead.

不带表达式的 throw 语句只能用在 catch 块中,在这种情况下,该语句会重新引发该 catch 块当前正在处理的异常。A throw statement with no expression can be used only in a catch block, in which case that statement re-throws the exception that is currently being handled by that catch block.

由于 throw 语句无条件地将控制转移到其他位置,因此无法访问 throw 语句的终点。Because a throw statement unconditionally transfers control elsewhere, the end point of a throw statement is never reachable.

当引发异常时,控制将转移到可处理异常的封闭 try 语句中的第一个 catch 子句。When an exception is thrown, control is transferred to the first catch clause in an enclosing try statement that can handle the exception. 从引发的异常点到将控件传输到适当的异常处理程序的时间点的过程称为 "异常传播"。The process that takes place from the point of the exception being thrown to the point of transferring control to a suitable exception handler is known as exception propagation. 异常的传播包括重复计算以下步骤,直到找到与异常匹配的 catch 子句。Propagation of an exception consists of repeatedly evaluating the following steps until a catch clause that matches the exception is found. 在此说明中,引发异常的位置最初为引发点的位置。In this description, the throw point is initially the location at which the exception is thrown.

  • 在当前函数成员中,将检查包含引发点的每个 try 语句。In the current function member, each try statement that encloses the throw point is examined. 对于 S的每个语句,从最内层的 try 语句开始,到最外面的 try 语句结束,计算以下步骤:For each statement S, starting with the innermost try statement and ending with the outermost try statement, the following steps are evaluated:

    • 如果 Stry 块包含了序列点,并且如果 S 具有一个或多个 catch 子句,则将根据 " Try" 语句部分中指定的规则,按显示顺序检查 catch 子句以查找异常的合适处理程序。If the try block of S encloses the throw point and if S has one or more catch clauses, the catch clauses are examined in order of appearance to locate a suitable handler for the exception, according to the rules specified in Section The try statement. 如果找到匹配的 catch 子句,则通过将控制转移到该 catch 子句的块来完成异常传播。If a matching catch clause is located, the exception propagation is completed by transferring control to the block of that catch clause.

    • 否则,如果 try 块或 catchS 包含了引发点,并且 S 有一个 finally 块,则将控制转移到 finally 块。Otherwise, if the try block or a catch block of S encloses the throw point and if S has a finally block, control is transferred to the finally block. 如果 finally 块引发另一个异常,则终止处理当前异常。If the finally block throws another exception, processing of the current exception is terminated. 否则,当控件到达 finally 块的终点时,将继续处理当前异常。Otherwise, when control reaches the end point of the finally block, processing of the current exception is continued.

  • 如果在当前函数调用中未找到异常处理程序,则终止函数调用,并发生以下情况之一:If an exception handler was not located in the current function invocation, the function invocation is terminated, and one of the following occurs:

    • 如果当前函数为非异步,则会为函数调用方重复上述步骤,并将引发点与调用函数成员的语句对应。If the current function is non-async, the steps above are repeated for the caller of the function with a throw point corresponding to the statement from which the function member was invoked.

    • 如果当前函数为 async 并返回任务,则会在返回任务中记录异常,该异常将被置于 "枚举器接口" 中所述的 "出错" 或 "已取消" 状态。If the current function is async and task-returning, the exception is recorded in the return task, which is put into a faulted or cancelled state as described in Enumerator interfaces.

    • 如果当前函数为 async 和 void 返回,则会通知当前线程的同步上下文,如可枚举接口中所述。If the current function is async and void-returning, the synchronization context of the current thread is notified as described in Enumerable interfaces.

  • 如果异常处理终止当前线程中的所有函数成员调用,指示该线程没有异常的处理程序,则该线程本身将终止。If the exception processing terminates all function member invocations in the current thread, indicating that the thread has no handler for the exception, then the thread is itself terminated. 此类终止的影响是由实现定义的。The impact of such termination is implementation-defined.

Try 语句The try statement

try 语句提供一种机制,用于捕获在执行块期间发生的异常。The try statement provides a mechanism for catching exceptions that occur during execution of a block. 此外,try 语句还可以指定在控制离开 try 语句时始终执行的代码块。Furthermore, the try statement provides the ability to specify a block of code that is always executed when control leaves the try statement.

try_statement
    : 'try' block catch_clause+
    | 'try' block finally_clause
    | 'try' block catch_clause+ finally_clause
    ;

catch_clause
    : 'catch' exception_specifier? exception_filter?  block
    ;

exception_specifier
    : '(' type identifier? ')'
    ;

exception_filter
    : 'when' '(' expression ')'
    ;

finally_clause
    : 'finally' block
    ;

有三种可能的 try 语句形式:There are three possible forms of try statements:

  • 后跟一个或多个 catch 块的 try 块。A try block followed by one or more catch blocks.
  • 后跟 finally 块的 try 块。A try block followed by a finally block.
  • 后跟一个或多个 catch 块后跟一个 finally 块的 try 块。A try block followed by one or more catch blocks followed by a finally block.

catch 子句指定一个exception_specifier时,该类型必须是 System.Exception、从 System.Exception 派生的类型或将 System.Exception (或子类)作为其有效基类的类型参数类型。When a catch clause specifies an exception_specifier, the type must be System.Exception, a type that derives from System.Exception or a type parameter type that has System.Exception (or a subclass thereof) as its effective base class.

catch 子句使用标识符同时指定exception_specifier时,将声明一个具有给定名称和类型的异常变量When a catch clause specifies both an exception_specifier with an identifier, an exception variable of the given name and type is declared. 异常变量对应于一个本地变量,该变量的作用域扩展了 catch 子句。The exception variable corresponds to a local variable with a scope that extends over the catch clause. exception_filter的执行期间,异常变量表示当前正在处理的异常。During execution of the exception_filter and block, the exception variable represents the exception currently being handled. 出于明确赋值检查的目的,在其整个范围内将异常变量视为明确赋值。For purposes of definite assignment checking, the exception variable is considered definitely assigned in its entire scope.

除非 catch 子句包含异常变量名称,否则无法访问筛选器和 catch 块中的异常对象。Unless a catch clause includes an exception variable name, it is impossible to access the exception object in the filter and catch block.

不指定exception_specifiercatch 子句称为一般 catch 子句。A catch clause that does not specify an exception_specifier is called a general catch clause.

某些编程语言可能会支持不能表示为派生自 System.Exception的对象的异常,但C#代码不会生成此类异常。Some programming languages may support exceptions that are not representable as an object derived from System.Exception, although such exceptions could never be generated by C# code. 可以使用常规 catch 子句来捕捉此类异常。A general catch clause may be used to catch such exceptions. 因此,一般的 catch 子句在语义上与指定类型 System.Exception的子句不同,因为前者还可以捕获其他语言的异常。Thus, a general catch clause is semantically different from one that specifies the type System.Exception, in that the former may also catch exceptions from other languages.

若要查找异常的处理程序,请按词法顺序检查 catch 子句。In order to locate a handler for an exception, catch clauses are examined in lexical order. 如果 catch 子句指定了一种类型,但没有指定异常筛选器,则在同一 try 语句中,后面的 catch 子句的编译时错误为指定与该类型相同或派生自该类型的类型。If a catch clause specifies a type but no exception filter, it is a compile-time error for a later catch clause in the same try statement to specify a type that is the same as, or is derived from, that type. 如果 catch 子句未指定任何类型并且没有筛选器,则它必须是该 try 语句的最后一个 catch 子句。If a catch clause specifies no type and no filter, it must be the last catch clause for that try statement.

catch 块中,不带表达式的 throw 语句(throw 语句)可用于重新引发由 catch 块捕获的异常。Within a catch block, a throw statement (The throw statement) with no expression can be used to re-throw the exception that was caught by the catch block. 对异常变量的赋值不会改变重新引发的异常。Assignments to an exception variable do not alter the exception that is re-thrown.

示例中In the example

using System;

class Test
{
    static void F() {
        try {
            G();
        }
        catch (Exception e) {
            Console.WriteLine("Exception in F: " + e.Message);
            e = new Exception("F");
            throw;                // re-throw
        }
    }

    static void G() {
        throw new Exception("G");
    }

    static void Main() {
        try {
            F();
        }
        catch (Exception e) {
            Console.WriteLine("Exception in Main: " + e.Message);
        }
    }
}

方法 F 捕获异常,将一些诊断信息写入控制台,更改异常变量,并重新引发异常。the method F catches an exception, writes some diagnostic information to the console, alters the exception variable, and re-throws the exception. 重新引发的异常是原始异常,因此生成的输出为:The exception that is re-thrown is the original exception, so the output produced is:

Exception in F: G
Exception in Main: G

如果第一个 catch 块引发 e 而不是重新引发当前异常,则生成的输出将如下所示:If the first catch block had thrown e instead of rethrowing the current exception, the output produced would be as follows:

Exception in F: G
Exception in Main: F

breakcontinuegoto 语句将控制转移到 finally 块,这是编译时错误。It is a compile-time error for a break, continue, or goto statement to transfer control out of a finally block. finally 块中出现 breakcontinuegoto 语句时,语句的目标必须在同一 finally 块中,否则将发生编译时错误。When a break, continue, or goto statement occurs in a finally block, the target of the statement must be within the same finally block, or otherwise a compile-time error occurs.

return 语句发生在 finally 块中是编译时错误。It is a compile-time error for a return statement to occur in a finally block.

执行 try 语句,如下所示:A try statement is executed as follows:

  • 控件传输到 try 块。Control is transferred to the try block.

  • 当和如果控件到达 try 块的终点时:When and if control reaches the end point of the try block:

    • 如果 try 语句具有 finally 块,则将执行 finally 块。If the try statement has a finally block, the finally block is executed.
    • Control 会传输到 try 语句的终点。Control is transferred to the end point of the try statement.
  • 如果在执行 try 块期间将异常传播到 try 语句:If an exception is propagated to the try statement during execution of the try block:

    • catch 子句(如果有)将按外观的顺序进行检查,以查找适用于异常的处理程序。The catch clauses, if any, are examined in order of appearance to locate a suitable handler for the exception. 如果 catch 子句未指定类型,或指定异常类型或异常类型的基类型:If a catch clause does not specify a type, or specifies the exception type or a base type of the exception type:
      • 如果 catch 子句声明异常变量,则会将异常对象分配给异常变量。If the catch clause declares an exception variable, the exception object is assigned to the exception variable.
      • 如果 catch 子句声明异常筛选器,则会计算筛选器。If the catch clause declares an exception filter, the filter is evaluated. 如果计算结果为 false,catch 子句不是匹配项,并且搜索将继续执行适用于适当处理程序的任何后续 catch 子句。If it evaluates to false, the catch clause is not a match, and the search continues through any subsequent catch clauses for a suitable handler.
      • 否则,catch 子句被视为匹配,并将控制转移到匹配的 catch 块。Otherwise, the catch clause is considered a match, and control is transferred to the matching catch block.
      • 当和如果控件到达 catch 块的终点时:When and if control reaches the end point of the catch block:
        • 如果 try 语句具有 finally 块,则将执行 finally 块。If the try statement has a finally block, the finally block is executed.
        • Control 会传输到 try 语句的终点。Control is transferred to the end point of the try statement.
      • 如果在执行 catch 块期间将异常传播到 try 语句:If an exception is propagated to the try statement during execution of the catch block:
        • 如果 try 语句具有 finally 块,则将执行 finally 块。If the try statement has a finally block, the finally block is executed.
        • 异常将传播到下一个封闭 try 语句。The exception is propagated to the next enclosing try statement.
    • 如果 try 语句没有 catch 子句,或者如果没有 catch 子句与异常匹配,则为:If the try statement has no catch clauses or if no catch clause matches the exception:
      • 如果 try 语句具有 finally 块,则将执行 finally 块。If the try statement has a finally block, the finally block is executed.
      • 异常将传播到下一个封闭 try 语句。The exception is propagated to the next enclosing try statement.

当控制离开 try 语句时,始终会执行 finally 块的语句。The statements of a finally block are always executed when control leaves a try statement. 无论是由于执行 breakcontinuegotoreturn 语句导致控件传输,还是由于将异常传播到 try 语句而导致的,都是如此。This is true whether the control transfer occurs as a result of normal execution, as a result of executing a break, continue, goto, or return statement, or as a result of propagating an exception out of the try statement.

如果在执行 finally 块期间引发了异常,并且未在同一 finally 块中捕获该异常,则该异常将传播到下一个封闭 try 语句。If an exception is thrown during execution of a finally block, and is not caught within the same finally block, the exception is propagated to the next enclosing try statement. 如果正在传播其他异常,则该异常将丢失。If another exception was in the process of being propagated, that exception is lost. 传播异常的过程将在 throw 语句(throw 语句)的说明中进一步讨论。The process of propagating an exception is discussed further in the description of the throw statement (The throw statement).

如果 try 语句可访问,则可访问 try 语句的 try 块。The try block of a try statement is reachable if the try statement is reachable.

如果 try 语句可访问,则可访问 try 语句 catch 块。A catch block of a try statement is reachable if the try statement is reachable.

如果 try 语句可访问,则可访问 try 语句的 finally 块。The finally block of a try statement is reachable if the try statement is reachable.

如果以下两个条件均为 true,则可以访问 try 语句的终结点:The end point of a try statement is reachable if both of the following are true:

  • try 块的终结点是可访问的,或者至少有一个 catch 块可访问的终结点。The end point of the try block is reachable or the end point of at least one catch block is reachable.
  • 如果存在 finally 块,则可访问 finally 块的终结点。If a finally block is present, the end point of the finally block is reachable.

Checked 和 unchecked 语句The checked and unchecked statements

checkedunchecked 语句用于控制整型算术运算和转换的溢出检查上下文The checked and unchecked statements are used to control the overflow checking context for integral-type arithmetic operations and conversions.

checked_statement
    : 'checked' block
    ;

unchecked_statement
    : 'unchecked' block
    ;

checked 语句导致在已检查的上下文中计算中的所有表达式,而 unchecked 语句导致在未检查的上下文中计算中的所有表达式。The checked statement causes all expressions in the block to be evaluated in a checked context, and the unchecked statement causes all expressions in the block to be evaluated in an unchecked context.

checkedunchecked 语句完全等效于checkedunchecked运算符(checked 和 unchecked 运算符),只不过它们在块而不是表达式上操作。The checked and unchecked statements are precisely equivalent to the checked and unchecked operators (The checked and unchecked operators), except that they operate on blocks instead of expressions.

Lock 语句The lock statement

lock 语句获取给定对象的互斥锁,执行语句,然后释放该锁。The lock statement obtains the mutual-exclusion lock for a given object, executes a statement, and then releases the lock.

lock_statement
    : 'lock' '(' expression ')' embedded_statement
    ;

lock 语句的表达式必须表示已知为reference_type的类型的值。The expression of a lock statement must denote a value of a type known to be a reference_type. 对于 lock 语句的表达式,不会执行任何隐式装箱转换(装箱转换),因此,表达式会出现编译时错误,以表示value_type的值。No implicit boxing conversion (Boxing conversions) is ever performed for the expression of a lock statement, and thus it is a compile-time error for the expression to denote a value of a value_type.

窗体的 lock 语句A lock statement of the form

lock (x) ...

其中 xreference_type的表达式,它完全等效于where x is an expression of a reference_type, is precisely equivalent to

bool __lockWasTaken = false;
try {
    System.Threading.Monitor.Enter(x, ref __lockWasTaken);
    ...
}
finally {
    if (__lockWasTaken) System.Threading.Monitor.Exit(x);
}

不同的是 x 只计算一次。except that x is only evaluated once.

持有互斥锁时,在同一执行线程中执行的代码也可以获取和释放锁。While a mutual-exclusion lock is held, code executing in the same execution thread can also obtain and release the lock. 但是,在其他线程中执行的代码被阻止获取锁定,直到锁定被释放。However, code executing in other threads is blocked from obtaining the lock until the lock is released.

不建议锁定 System.Type 对象以同步对静态数据的访问。Locking System.Type objects in order to synchronize access to static data is not recommended. 其他代码可能会锁定同一类型,这可能会导致死锁。Other code might lock on the same type, which can result in deadlock. 更好的方法是通过锁定专用静态对象来同步对静态数据的访问。A better approach is to synchronize access to static data by locking a private static object. 例如:For example:

class Cache
{
    private static readonly object synchronizationObject = new object();

    public static void Add(object x) {
        lock (Cache.synchronizationObject) {
            ...
        }
    }

    public static void Remove(object x) {
        lock (Cache.synchronizationObject) {
            ...
        }
    }
}

using 语句The using statement

using 语句获取一个或多个资源,执行语句,然后释放资源。The using statement obtains one or more resources, executes a statement, and then disposes of the resource.

using_statement
    : 'using' '(' resource_acquisition ')' embedded_statement
    ;

resource_acquisition
    : local_variable_declaration
    | expression
    ;

资源是实现 System.IDisposable的类或结构,其中包括一个名为 Dispose的无参数方法。A resource is a class or struct that implements System.IDisposable, which includes a single parameterless method named Dispose. 使用资源的代码可以调用 Dispose,以指示不再需要资源。Code that is using a resource can call Dispose to indicate that the resource is no longer needed. 如果未调用 Dispose,则最终将作为垃圾回收的结果发生。If Dispose is not called, then automatic disposal eventually occurs as a consequence of garbage collection.

如果local_variable_declaration resource_acquisition的形式,则local_variable_declaration的类型必须是 dynamic 或可隐式转换为 System.IDisposable的类型。If the form of resource_acquisition is local_variable_declaration then the type of the local_variable_declaration must be either dynamic or a type that can be implicitly converted to System.IDisposable. 如果resource_acquisition的形式为expression ,则此表达式必须可隐式转换为 System.IDisposableIf the form of resource_acquisition is expression then this expression must be implicitly convertible to System.IDisposable.

resource_acquisition中声明的局部变量是只读的,并且必须包含初始值设定项。Local variables declared in a resource_acquisition are read-only, and must include an initializer. 如果嵌入的语句尝试修改这些本地变量(通过赋值或 ++-- 运算符),则会发生编译时错误,请获取它们的地址,或将其作为 refout 参数传递。A compile-time error occurs if the embedded statement attempts to modify these local variables (via assignment or the ++ and -- operators) , take the address of them, or pass them as ref or out parameters.

using 语句转换为三个部分:获取、使用和处理。A using statement is translated into three parts: acquisition, usage, and disposal. 资源的使用隐式包含在包含 finally 子句的 try 语句中。Usage of the resource is implicitly enclosed in a try statement that includes a finally clause. finally 子句将释放资源。This finally clause disposes of the resource. 如果获取 null 资源,则不会调用任何 Dispose,并且不会引发异常。If a null resource is acquired, then no call to Dispose is made, and no exception is thrown. 如果资源的类型为 dynamic 则通过隐式动态转换(隐式动态转换)将其动态转换为在获取期间 IDisposable,以确保转换在使用和处置之前成功。If the resource is of type dynamic it is dynamically converted through an implicit dynamic conversion (Implicit dynamic conversions) to IDisposable during acquisition in order to ensure that the conversion is successful before the usage and disposal.

窗体的 using 语句A using statement of the form

using (ResourceType resource = expression) statement

对应于三个可能的扩展中的一个。corresponds to one of three possible expansions. 如果 ResourceType 是不可为 null 的值类型,则扩展为When ResourceType is a non-nullable value type, the expansion is

{
    ResourceType resource = expression;
    try {
        statement;
    }
    finally {
        ((IDisposable)resource).Dispose();
    }
}

否则,当 ResourceType 是可以为 null 的值类型或引用类型而不是 dynamic时,展开是Otherwise, when ResourceType is a nullable value type or a reference type other than dynamic, the expansion is

{
    ResourceType resource = expression;
    try {
        statement;
    }
    finally {
        if (resource != null) ((IDisposable)resource).Dispose();
    }
}

否则,当 dynamic``ResourceType 时,展开是Otherwise, when ResourceType is dynamic, the expansion is

{
    ResourceType resource = expression;
    IDisposable d = (IDisposable)resource;
    try {
        statement;
    }
    finally {
        if (d != null) d.Dispose();
    }
}

在任一扩展中,resource 变量在嵌入语句中是只读的,并且在嵌入的语句中不可访问 d 变量,并且不可见。In either expansion, the resource variable is read-only in the embedded statement, and the d variable is inaccessible in, and invisible to, the embedded statement.

允许实现以不同方式实现给定的 using 语句(例如出于性能原因),前提是该行为与上述扩展一致。An implementation is permitted to implement a given using-statement differently, e.g. for performance reasons, as long as the behavior is consistent with the above expansion.

窗体的 using 语句A using statement of the form

using (expression) statement

具有相同的三个可能的扩展。has the same three possible expansions. 在这种情况下 ResourceType 隐式编译时 expression类型(如果有)。In this case ResourceType is implicitly the compile-time type of the expression, if it has one. 否则,接口 IDisposable 自身用作 ResourceTypeOtherwise the interface IDisposable itself is used as the ResourceType. 在嵌入的语句中,不能访问 resource 变量,并且该变量不可见。The resource variable is inaccessible in, and invisible to, the embedded statement.

如果resource_acquisition采用local_variable_declaration的形式,则可以获取给定类型的多个资源。When a resource_acquisition takes the form of a local_variable_declaration, it is possible to acquire multiple resources of a given type. 窗体的 using 语句A using statement of the form

using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statement

完全等效于一系列嵌套的 using 语句:is precisely equivalent to a sequence of nested using statements:

using (ResourceType r1 = e1)
    using (ResourceType r2 = e2)
        ...
            using (ResourceType rN = eN)
                statement

下面的示例创建一个名为 log.txt 的文件,并向该文件写入两行文本。The example below creates a file named log.txt and writes two lines of text to the file. 然后,该示例将打开该文件以进行读取,并将包含的文本行复制到控制台。The example then opens that same file for reading and copies the contained lines of text to the console.

using System;
using System.IO;

class Test
{
    static void Main() {
        using (TextWriter w = File.CreateText("log.txt")) {
            w.WriteLine("This is line one");
            w.WriteLine("This is line two");
        }

        using (TextReader r = File.OpenText("log.txt")) {
            string s;
            while ((s = r.ReadLine()) != null) {
                Console.WriteLine(s);
            }

        }
    }
}

由于 TextWriterTextReader 类实现了 IDisposable 接口,因此该示例可以使用 using 语句,以确保在执行写入或读取操作后正确关闭基础文件。Since the TextWriter and TextReader classes implement the IDisposable interface, the example can use using statements to ensure that the underlying file is properly closed following the write or read operations.

Yield 语句The yield statement

在迭代器块()中使用 yield 语句,以向迭代器的枚举器对象(枚举器对象)或可枚举对象(可枚举对象)生成值或表示迭代结束。The yield statement is used in an iterator block (Blocks) to yield a value to the enumerator object (Enumerator objects) or enumerable object (Enumerable objects) of an iterator or to signal the end of the iteration.

yield_statement
    : 'yield' 'return' expression ';'
    | 'yield' 'break' ';'
    ;

yield 不是保留字;仅当紧靠在 returnbreak 关键字之前使用时,它才具有特殊意义。yield is not a reserved word; it has special meaning only when used immediately before a return or break keyword. 在其他上下文中,yield 可以用作标识符。In other contexts, yield can be used as an identifier.

yield 语句可以出现的位置有多个限制,如下所述。There are several restrictions on where a yield statement can appear, as described in the following.

  • 如果 yield 语句(窗体)出现在method_body外, operator_bodyaccessor_body将出现编译时错误。It is a compile-time error for a yield statement (of either form) to appear outside a method_body, operator_body or accessor_body
  • 在匿名函数内出现的 yield 语句(任一窗体)的编译时错误。It is a compile-time error for a yield statement (of either form) to appear inside an anonymous function.
  • try 语句的 finally 子句中出现 yield 语句(窗体)的编译时错误。It is a compile-time error for a yield statement (of either form) to appear in the finally clause of a try statement.
  • 如果 yield return 语句出现在包含任何 catch 子句的 try 语句中的任何位置,则会发生编译时错误。It is a compile-time error for a yield return statement to appear anywhere in a try statement that contains any catch clauses.

下面的示例演示 yield 语句的一些有效和无效用法。The following example shows some valid and invalid uses of yield statements.

delegate IEnumerable<int> D();

IEnumerator<int> GetEnumerator() {
    try {
        yield return 1;        // Ok
        yield break;           // Ok
    }
    finally {
        yield return 2;        // Error, yield in finally
        yield break;           // Error, yield in finally
    }

    try {
        yield return 3;        // Error, yield return in try...catch
        yield break;           // Ok
    }
    catch {
        yield return 4;        // Error, yield return in try...catch
        yield break;           // Ok
    }

    D d = delegate { 
        yield return 5;        // Error, yield in an anonymous function
    }; 
}

int MyMethod() {
    yield return 1;            // Error, wrong return type for an iterator block
}

隐式转换(隐式转换)必须从 yield return 语句中表达式的类型到迭代器的 yield 类型(yield 类型)存在。An implicit conversion (Implicit conversions) must exist from the type of the expression in the yield return statement to the yield type (Yield type) of the iterator.

执行 yield return 语句,如下所示:A yield return statement is executed as follows:

  • 计算语句中给定的表达式,将其隐式转换为 yield 类型,并将其分配给枚举器对象的 Current 属性。The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object.
  • 迭代器块的执行被挂起。Execution of the iterator block is suspended. 如果 yield return 语句位于一个或多个 try 块中,则不会执行关联的 finally 块。If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time.
  • 枚举器对象的 MoveNext 方法将 true 返回到其调用方,指示枚举器对象已成功地前进到下一项。The MoveNext method of the enumerator object returns true to its caller, indicating that the enumerator object successfully advanced to the next item.

对枚举器对象的 MoveNext 方法的下一次调用会从其最后挂起的位置继续执行迭代器块。The next call to the enumerator object's MoveNext method resumes execution of the iterator block from where it was last suspended.

执行 yield break 语句,如下所示:A yield break statement is executed as follows:

  • 如果 yield break 语句由一个或多个具有关联 finally 块的 try 块括起来,则控件最初会传输到最内部 try 语句的 finally 块。If the yield break statement is enclosed by one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. 当和如果控件到达 finally 块的终点时,控制将转移到下一个封闭 try 语句的 finally 块。When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. 此过程将重复进行,直到执行完所有封闭 try 语句 finally 块。This process is repeated until the finally blocks of all enclosing try statements have been executed.
  • 控制权将返回给迭代器块的调用方。Control is returned to the caller of the iterator block. 这是枚举器对象的 MoveNext 方法或 Dispose 方法。This is either the MoveNext method or Dispose method of the enumerator object.

由于 yield break 语句无条件地将控制转移到其他位置,因此无法访问 yield break 语句的终点。Because a yield break statement unconditionally transfers control elsewhere, the end point of a yield break statement is never reachable.