C# Language Specification 5.0 (翻译)第五章 变量

变量(variable)表示存储的位置。每个变量都有类型,类型决定变量保存的值的类型。C# 是一门类型安全的语言,C# 编译器会确保变量中保存一个适合类型的值。变量的值可通过赋值或通过使用 ++ 与 -- 操作符改变。
变量必须在获得(obtained)前被明确赋值(definitely assigned)(第五章第三节)。
如以下部分所述,变量要么初始已赋值(initially assigned),要么初始未赋值(initially unassigned)。初始已赋值的变量有非常明确(well-defined)的初始值,且视己为被明确赋值(definitely assigned)的。初始未赋值的变量没有初始值(initial value)。对于初始未赋值变量来说,为了在某个位置能明确赋值,必须在通往该位置的每一个可能执行到的分支对该变量赋值。
变量种类
C# 定义了七种变量:静态变量(static variables)、实例变量(instance variables)、数组元素(array elements)、值参数(value parameters)、引用参数(reference parameters)、输出参数(output parameters)和局部变量(local variables)。本节将对上述类型逐一介绍。
下例中
class A
{
    public static int x;
    int y;
    void F(int[] v, int a, ref int b, out int c) {
        int i = 1;
        c = a + b++;
    }
}
x 是静态变量,y 是实例变量,v[0] 是数组元素,a 是值参数,b 是引用参数,c 是输出参数,i 是局部变量。
静态变量
以静态修饰符 static 声明的字段被称为静态变量(static variable)。静态变量在其所在类型的静态构造函数(第十章第十二节)执行之前便已存在,当其所相关的应用域(application domain)退出时不再存在。
静态变量的初始值是其类型的默认值(第五章第二节)。
为了明确赋值检查,静态变量被视为初始已赋值的。
实例变量
不以静态修饰符 static 声明的字段叫做实例变量(instance variable)。
类中的实例变量
类的实例变量在该类被创建实例时开始存在,当所有对该实例的引用都终止、实例析构函数(若有)执行过后,该类的实例变量不再存在。
类实例变量的初始值是该变量类型的默认值(第五章第二节)。
为了明确赋值检查,类的实例变量被认为是初始已赋值的。
结构中的实例变量
结构实例变量的生命周期与其所属的结构变量是一样的。换句话说,当结构类型的变量存在或终止时,它的实例变量也随之存在或消失。
结构的实例变量的初始赋值状态与其所在的结构变量一样。换句话说,当结构变量被视为初始化已赋值时,其实例变量也被视为初始化已赋值;当结构变量被视为初始化未赋值时,其实例变量也为初始化未赋值。
数组元素
当数组实例被创建时,数组元素开始存在;当没有任何引用指向该数组实例时,数组元素消失。
数组的每一个元素的初始值均为该数组元素类型的默认值(第五章第二节)。
为了明确赋值检查,数组元素被认为是初始已赋值的。
值参数
不使用 ref 或 out 修饰符的参数声明叫做值参数(value parameter)。
值形参再调用该参数所属的函数成员(方法、实例构造函数、访问器或操作符)或匿名函数时开始存在,并由调用者提供的实参的值初始化。一般来说,值形参的存在直到函数成员或匿名函数返回结果为止。然而,如果值形参如果被匿名函数(第七章第十五节)捕获(capture),则其生命周期将被至少延长到由该匿名函数创建的委托或表达式树可被垃圾回收为止。
为了明确赋值检查,值参数被认为是初始已赋值的。
引用参数
使用 ref 修饰符的参数声明叫做引用参数(reference parameter)。
引用形参不会创建新的本地存储位置。相反,引用形参表示其存储位置与函数成员或匿名函数调用所给定的实参的存储位置是一样的。因此,引用形参的值总是与基础变量相同。
下面的明确赋值规则适用于引用形参。注意,对输出形参(output parameters)规则与第五章第 1.6 节中所描述的规则不同。
- 变量必须在它被以引用参数形式传入函数成员或委托调用之前被明确赋值(第五章第三节)。
 - 在函数成员或匿名函数内,引用形参被视为已被赋值。
 
在结构类型的实例方法或实例访问器中,this 关键字的行为与该结构类型所引用的形参相同(第七章第 6.7 节)。
输出参数
使用 out 修饰符的参数声明叫做输出参数(output parameter)。
输出形参不会创建新的本地存储位置。相反,输出形参表示与函数成员或委托调用所给定的存储位置是一样的。因此,输出形参的值总是与基础变量相同。
下面的明确赋值规则适用于输出形参。注意,对引用形参(reference parameters)规则与第五章第 1.5 节中所描述的规则不同。
- 变量不必在以输出形参的方式传入函数成员或委托调用之前明确初始化。
 - 在正常完成函数成员或委托调用之后,每一个以输出形参的形式传入的变量都被认为在执行路径(execution path)中赋值。
 - 在函数成员或匿名函数内部,输出形参被视为初始未赋值。
 - 每一个函数成员或匿名函数的输出形参必须在函数成员或匿名函数正常返回结果之前被明确赋值(第五章第三节)。
 
在结构类型的实例方法中,this 关键字的行为与该结构类型所输出的形参相同(第七章第 6.7 节)。
局部变量
局部变量(local variable)由 local-variable-declaration 所声明,可出现在 block、for-statement、switch-statement 或 using-statement 内;或由 foreach-statement 或 specific-catch-clause 的 try-statement 声明。
局部变量的生命周期是程序执行期间的一部分,在此期间定会为其保留存储。这个生命周期至少从进入相关的 block、for-statement、switch-statement、using-statement、foreach-statement 或 specific-catch-clause 开始,到该 block、for-statement、switch-statement、using-statement、foreach-statement 或 specific-catch-clause 以任何方式结束为止。(进入闭包的 block 或方法调用会挂起(suspends)——但不会结束——当前的 block、for-statement、switch-statement、using-statement、foreach-statement 或 specific-catch-clause。)如果局部变量被匿名函数(第七章第 15.5.1 节)捕获,其生命周期至少延长到从该匿名函数创建的委托或表达式树,以及其它引用该捕获变量的对象可被垃圾回收为止。
如果递归地进入(entered recursively)父 block、for-statement、switch-statement、using-statement、foreach-statement 或 specific-catch-clause,每次都会创建该局部变量的新实例,并重新计算其 local-variable-initializer(若有)。
由 local-variable-declaration 引入的局部变量不会自动初始化,因此没有默认值。为了明确赋值检查的目的,由 local-variable-declaration 引入的局部变量被认为是初始未赋值(initially unassigned)的。local-variable-declaration 可以包括 local-variable-initializer,在此情况下,位于初始化表达式(第五章第 3.3.4 节)之后的变量被视作明确赋值的。
由 local-variable-declaration 引入的局部变量的范围内,在 local-variable-declarator 文本位置之前引用该局部变量会导致「编译时错误」。如果局部变量的声明是隐式的(第八章第 5.1 节),那么在 local-variable-declarator 内引用该变量同样会报错。
由 foreach-statement 或 specific-catch-clause 引入的局部变量在整个范围内被视作明确赋值的。
局部变量的实际生命周期是依赖于具体实现的(implementation-dependent)。比方说,编译器可能静态地(statically)决定某个块中的局部变量只用于该块的一小部分(a small portion of that block)。基于此分析,编译器生成的代码可能会使该变量的存储生命周期短于其所在的块。
局部引用变量所引用的存储(storage)的回收(reclaimed)独立于该局部引用变量(第三章第九节)的生命周期
默认值
下列分类的变量将自动初始化其默认值:
- 静态变量;
 - 类实例的实例变量;
 - 数组元素。
 
变量默认值依赖于其变量类型,并由以下规定确定:
- 对于值类型的变量,默认值与该值类型的默认构造函数(第四章第 1.2 节)所计算出的值相同;
 - 对于引用类型的变量,默认值为空 null。
 
默认值初始化是一般是内存管理器(memory manager)或垃圾回收器(garbage collector)在分配内存给其使用之前,将内存的「所有位置零(all-bits-zero)」。基于此,很方便使用「所有位置零」表示空引用(null reference)。
明确赋值
在函数成员可执行代码中的给定位置,如果编译器能够通过特别的静态流程分析(particular static flow analysis,第五章第 3.3 节)证明变量已被自动初始化(automatically initialized)或至少一次被作为赋值的目标,那么称变量已被明确赋值。非正式地来讲,明确赋值的规则如下:
- 初始已赋值变量(第五章第 3.1 节)被视作明确赋值的。
 - 如果所有可能的通向指定位置的执行路径至少包含以下一条,那么称初始未赋值变量(第五章第 3.2 节)可被视为明确赋值的:
- 将变量作为左操作数(left operand)进行简单赋值(simple assignment,第七章第 17.1 节)。
 - 将变量传输到输出参数作为调用表达式(invocation expression,第七章第 6.5 节)或对象创建表达式(object creation expression,第七章第 6.10.1 节)。
 - 对于局部变量,包含变量初始化器(variable initializer)的局部变量声明(第八章第 5.1 节)。
 
 
对于以上这些非正式的规则的正式规范在第五章第 3.1 节、第五章第 3.2 节以及第五章第 3.3 节中介绍。
对于 struct-type 变量之实例变量的明确赋值状态,既可以单独跟踪(tracked individually),也可以整体跟踪(tracked collectively)。另外除上述规则外,下列规则也将应用于 struct-type 变量及其实例变量:
- 当实例变量包含已被视作明确赋值的 
struct-type变量时,被视作已被明确赋值; - 当 
struct-type变量的每个实例变量都视作明确赋值时,它也被视作明确赋值。 
下列上下文是必须明确赋值的:
- 变量必须在被获得之前的每一个地方明确赋值,以确保不会出现未定义值。表达式中出现的变量被视作获取变量的值,除非当:
- 变量时简单赋值的左操作数,
 - 变量传递为一个输出参数,或者
 - 变量是 
struct-type变量且作为成员访问的左操作数出现。 
 - 变量必须在作为引用参数(reference parameter)传递的每个位置明确赋值,以确保被调用的函数成员能够调用将之视作初始已赋值。
 - 所有函数成员的输出参数必须在函数成员返回的每一个位置上明确赋值(通过 
return语句或通过执行到函数成员体的尾部并返回),以确保函数成员不会在输出参数中返回未定义的值(undefined values),因此允许编译器把将变量作为输出参数的函数成员的调用视作等价于对变量的赋值。 struct-type实例构造函数的this变量必须在实例构造函数所返回的每一个位置上明确赋值。
初始已赋值变量
以下变量分类被归入初始已赋值(initially assigned):
- 静态变量
 - 类实例的实例变量
 - 初始已赋值的结构变量的实例变量
 - 数组元素
 - 值参数
 - 引用参数
 - 在 catch 子句 或 foreach 语句中的变量声明
 
初始未赋值变量
以下变量分类被归入初始未赋值(initially unassigned):
- 初始未赋值的结构变量的实例变量
 - 输出参数,包括结构实例构造函数的 
this变量 - 局部变量,除了那些声明于 catch 子句或 foreach 语句的变量
 
明确赋值的详细规则
为了确定每个所用变量是否已明确赋值,编译器必须使用与本节所述之一等价的处理过程。
编译器会处理每一个具有至少一个初始未赋值变量的函数成员的主体。对于每一个初始未赋值的变量 v,编译器在以下函数成员内节点确定其明确赋值状态:
- 位于每句语句的开头位置
 - 位于每句语句的结束点(end point,第八章第一节)
 - 在每个将控制转移到另一句语句或语句结束点的 arc(Automatic Reference Counting)[1] 上
 - 位于每个表达式的开头位置
 - 位于每个表达式的结尾位置
 
v 的明确赋值状态可以是:
- 明确赋值的(definitely assigned)。这表明在所有可以到达该点的控制流中,
v都已经赋值。 - 未明确赋值的(not definitely assigned)。对于位于 bool 类型表达式结尾处变量状态,其变量状态为未明确赋值的可能(但不一定)分成下列子状态(sub-states):
- 在 true 表达式后明确赋值。这个状态表明如果 bool 表达式运算结果为 true 时 
v将明确赋值,但当其运算结果为 false 时则不一定。 - 在 false 表达式时候明确赋值。这个状态表明如果 bool 表达式运算结果为 false 时 
v将明确赋值,但当其运算结果为 true 时则不一定。 
 - 在 true 表达式后明确赋值。这个状态表明如果 bool 表达式运算结果为 true 时 
 
下列规则控制变量 v 在每一个位置上如何决定明确赋值状态的。
一般语句规则
- 位于函数成员主体开头处的 v 是未明确赋值的;
 - 在任何无法访问的语句的开头处的 v 都是明确赋值的;
 - 在任何其它语句开头处 v 的明确赋值状态由检查 v 在所有指向该语句开始处的控制流转移上的明确赋值状态所决定的。当且仅当 v 在所有此种控制流转移上是明确赋值的,那么 v 才在语句开头处是明确赋值的。确定所有可能的控制流转移集的方法与检查语句可访问性的方法(第八章第一节)相同。
 - 在 block、checked、unchecked、if、while、do、for、foreach、lock、using 或 switch 语句结尾点上的 v 的明确赋值状态由检查所有指向该语句结尾点的控制流转移上 v 明确服装状态所决定。如果 v 在所有此种控制流转移上是明确赋值的,那么 v 才在语句结尾点上是明确赋值的。不然的话,v 在语句结尾点上是未明确赋值的。确定所有可能的控制流转移集的方法与检查语句可访问性的方法(第八章第一节)相同。
 
块、checked 与 unchecked 语句
在指向某块(block)语句列表(statement list)中第一句语句(如果语句列表为空(empty)则指向该块结束点)的控制转移上 v 的明确赋值状态与语句、checked 或 unchecked 语句之前的 v 的明确赋值状态一致。
表达式语句
对于由表达式 expr 组成的表达式语句 stmt:
- 位于 
expr开头位置的 v 的明确赋值状态与stmt开头处的一致; - 如果 v 在 
expr结尾处是明确赋值的,那么在stmt结尾点也是明确赋值的,不然相反。 
声明语句
- 如果 
stmt是不带初始化器的声明语句,则 v 在stmt结束点与在stmt开头处具有相同的明确赋值状态; - 如果 
stmt是一个带初始化器的声明语句,则确定 v 的明确赋值状态时可将之视为语句列表,每个带初始化器的声明对应一句赋值语句(按声明的顺序)。 
if 语句
对于形如以下的 if 语句 stmt:
if ( expr ) then-stmt else else-stmt
- 位于 
expr开头位置的 v 的明确赋值状态与stmt开头处一致; - 如果 v 在 
expr结尾处是明确赋值的,那么在指向then-stmt以及任意一个else-stmt或stmt结尾点(如果没有 else 子句)的控制流转移上是明确赋值的; - 如果 v 在 
expr结尾处的状态是「在 true 表达式之后明确赋值」,那么在指向then-stmt的控制流转移上是明确赋值的,但指向else-stmt或stmt的结尾点(如果没有 else 子句)的控制流转移上是未明确赋值的; - 如果 v 在 
expr结尾点的状态是「在 false 表达式之后明确赋值」,那么在指向else-stmt的控制流转移上是明确赋值的,但指向then-stmt的控制流转移上是未明确赋值的。位于stmt结束点(当且仅当在then-stmt结尾点是明确赋值的)上是明确赋值的。 - 否则的话,位于指向任意一个 
then-stmt、else-stmt或stmt结尾点(如果没有 else 子句)的控制流转移上的 v 被视作未明确赋值。 
switch 语句
在带有控制表达式 expr 的 switch 语句 stmt:
- 位于 
expr开始位置的 v 的明确赋值状态与位于stmt开头位置的 v 的状态是一致的; - 在指向一个可访问(reachable)的 switch 语句块列表的控制流转移上 v 明确赋值状态就是 
expr结尾处的明确赋值状态。 
while 语句
对于形如以下的 while 语句 stmt:
while ( expr ) while-body
- 位于 
expr开头位置的 v 的明确赋值状态与stmt开头处的一致; - 如果 v 在 
expr结尾处是明确赋值的,那么在指向while-body的控制流转移上以及在stmt结尾点上的状态是明确赋值的; - 如果 v 在 
expr结尾点的状态是「在 true 表达式之后明确赋值」,那么在指向while-body的控制流转移上它是明确赋值的,但在stmt结尾点上是未明确赋值的; - 如果 v 在 
expr结尾点的状态是「在 false 表达式之后明确赋值」,那么在指向stmt结尾点的控制流转移上它是明确赋值的,单位指向while-body的控制流转移上它未明确赋值的。 
do 语句
对 do 语句的明确赋值检查有以下形式:
do do-body while ( expr ) ;
- 位于 
stmt开头处到do-body的控制流转移(control flow transfer)上的 v 的明确赋值状态与stmt开头处的一致。 - 位于 
expr开头处的 v 的明确赋值状态与do-body结尾处的一致。 - 如果位于 
expr结尾处 v 是明确赋值的,那么在stmt结尾处的控制硫转移上的 v 也是明确赋值的。 - 如果位于 
expr结尾处 v 具有「在 false 表达式之后明确赋值」,那么在stmt结尾处的控制硫转移上的 v 也是明确赋值的。 
for 语句
对 for 语句的明确赋值检查有以下形式:
for ( for-initializer ; for-condition ; for-iterator ) embedded-statement
如下面所写语句一样:
{
    for-initializer ;
    while ( for-condition ) {
        embedded-statement ;
        for-iterator ;
    }
}
如果 for-condition 从语句中省略,则当评估明确赋值状态时,可将上述展开语句中的 for-condition 当做 true。
break、continue 与 goto 语句
由 break、continue 或 goto 语句导致的控制流转移(control flow transfer)上 v 的明确赋值状态与语句开始处 v 的明确赋值状态一致。
throw 语句
对于以下形式的语句 stmt:
throw expr ;
位于 expr 开头处 v 的明确赋值状态与 stmt 开头处 v 的状态一致。
return 语句
对于以下形式的语句 stmt:
return expr ;
- 位于 
expr开头 v 的明确赋值状态与stmt开头 v 的状态是一致的。 - 如果 v 是输出形参,则其必须明确赋值(二选一):
- 要么在 
expr之后; - 要么在闭包于 return 语句的 
try-finally或try-catch-finally的finally块的结尾处。 
 - 要么在 
 
对于以下形式的语句 stmt:
return ;
- 如果 v 是输出形参,则其必须明确赋值(二选一):
- 要么在 
stmt之前; - 要么在闭包于 
return语句的try-catch或try-catch-finally的finally块的结尾处。 
 - 要么在 
 
try-catch 语句
对于以下形式的语句 stmt:
try try-block
catch(...) catch-block-1
...
catch(...) catch-block-n
- 位于 
try-block开始处的 v 的明确赋值状态与stmt开头处一致; - 位于 
catch-block-i开始处(对于任意 i)的 v 的明确赋值状态与stmt开头处一致; - 当且仅当 v 位于 
try-block结尾点和每一个catch-block-i(i ∈ [1, n])的结尾点是明确赋值的,则位于stmt结尾点的 v 的明确赋值状态是明确赋值的。 
try-finally 语句
对于以下形式的 try 语句 stmt:
try try-block finally finally-block
- 位于 
try-block开始处的 v 的明确赋值状态与stmt开头处一致; - 位于 
finally-block开始处的 v 的明确赋值状态与stmt开头处一致; - 当且仅当至少满足以下一条条件时,位于 
stmt结尾点的 v 的明确赋值状态是明确赋值的:- v 在 
try-block结束点是明确赋值的; - v 在 
finally-bolck结束点是明确赋值的。 
 - v 在 
 
如果控制流转移(比方说,一个 goto 语句)从 try-block 内开始,结束于 try-block 外,那么如果 v 在 finally-block 的结束点上明确赋值,在控制流转移上 v 同样被视作明确赋值的(这不是必要条件,如果 v 在 finally-block 结束点上是明确赋值的,那么它任被视作明确赋值)。
try-catch-finally 语句
对于形如下面这段 try-catch-finally 的明确赋值分析
try try-block
catch(...) catch-block-1
...
catch(...) catch-block-n
finally finally-block
与将 try-catch 语句闭包于 try-finally 内的效果一样:
try {
    try try-block
    catch(...) catch-block-1
    ...
    catch(...) catch-block-n
}
finally finally-block
下例演示了明确赋值在不同 try 语句块(第八章第十节)中的效果。
class A
{
    static void F() {
        int i, j;
        try {
            goto LABEL;
            // i 和 j 都没有明确赋值
            i = 1;
            // i 明确赋值
        }
        catch {
            // i 和 j 都没有明确赋值
            i = 3;
            // i 明确赋值
        }
        finally {
            // i 和 j 都没有明确赋值
            j = 5;
            // j 明确赋值
        }
        // i and j 明确赋值
        LABEL:;
        // j 明确赋值
	}
}
foreach 语句
对于以下形式的 foreach 语句 stmt:
foreach ( type identifier in expr ) embedded-statement
- 位于 
expr开头处的 v 的明确赋值状态与stmt开头处的一致; - 位于指向 
embedded-statement的控制流转移上的 v 的明确赋值状态与expr结尾处的一致。 
using 语句
对于以下形式的 using 语句 stmt:
using ( resource-acquisition ) embedded-statement
- 位于 
resource-acquisition开头处的 v 的明确赋值状态与stmt开头处的一致; - 位于指向 
embedded-statement的控制流转移上的 v 的明确赋值状态与resource-acquisition结尾处的一致。 
lock 语句
对于以下形式的 lock 语句 stmt:
lock ( expr ) embedded-statement
- 位于 
expr开头处的 v 的明确赋值状态与stmt开头处的一致; - 位于指向 
embedded-statement的控制流转移上的 v 的明确赋值状态与expr结尾处的一致。 
yield 语句
对于以下形式的 yield return 语句 stmt:
yield return expr ;
- 位于 
expr开头 v 的明确赋值状态与stmt开头 v 的状态是一致的。 - 位于 
stmt结尾 v 的明确赋值状态与expr结尾 v 的状态是一致的。 
yield break 语句对于明确定义状态没有影响。
简单表达式的一般规则
以下规则可应用于下诸类型之表达式:文本(literals,第七章第 6.1 节)、简单名称(simple names,第七章第 6.2 节)、成员访问表达式(member access expressions,第七章第 6.4 节)、非索引基访问表达式(non-indexed base access expressions,第七章第 6.8 节)、typeof 表达式(typeof expressions,第七章第 6.11 节)以及默认值表达式(default value expressions,第七章第 6.13 节)。
- 位于上述表达式结尾处 v 的明确赋值状态与位于表达式开头的 v 的状态一致。
 
带嵌入表达式的表达式一般规则
以下规则可应用于下诸类型之表达式:带括号的表达式(parenthesized expressions,第七章第 6.3 节)、元素访问表达式(element access expressions,第七章第 6.6 节)、带索引的基访问表达式(base access expressions with indexing,第七章第 6.8 节)、增量与减量表达式(increment and decrement expressions,第七章第 6.9 节)、强制转换表达式(cast expressions,第七章第 7.6 节)、一元 +, -, ~, * 表达式、二元 +, -, *, /, %, <<, >>, <, <=, >, >=, ==, !=, is, as, &, |, ^ 表达式(第七章第 8、9、10、11 节)、复合赋值表达式(compound assignment expressions, 第七章第 17.2 节)、checked 与 unchecked 表达式(第七章第 6.12 节)以及数组与委托创建表达式(array and delegate creation expressions,第七章第 6.10 节)。
这些表达式包含一个或多个固定顺序无条件计算(unconditionally evaluated in a fixed order)的子表达式(sub-expressions)。比方说,二元运算符 % 先运算左边的值,然后运算右边的。索引操作先计算索引表达式(indexed expression),然后从左到右运算每个索引表达式(index expressions)。对于具有子表达式 expr1、expr2、……、exprn 的表达式 expr,按下列顺序执行:
- 位于 expr1 开头的 v 的明确赋值状态与 
expr开头的状态一致; - 位于 expri(i 大于 1) 开头的 v 的明确赋值状态与 exprn 结尾处 v 的状态一致;
 - 位于 expr` 结尾处 v 的明确赋值状态与 exprn 结尾处 v 的状态一致。
 
调用表达式与对象创建表达式
对于以下形式的调用表达式(invocation expression)expr:
primary-expression ( arg1 , arg2 , … , argn )
或以下形式的对象创建表达式(object creation expression):
new type ( arg1 , arg2 , … , argn )
- 对于调用表达式,位于 
primary-expression前 v 的明确赋值状态与expr前 v 的状态一致。 - 对于调用表达式,位于 arg1 之前 v 的明确赋值状态与 
primary-expression后 v 的状态是一致的。 - 对于对象创建表达式,位于 arg1 之前 v 的明确赋值状态与 
expr后 v 的状态是一致的。 - 对于每个 argi 实参,位于 argi 之后 v 的明确赋值状态是由标准表达式规则(normal expression rules)所决定的,其中忽略所有的 
ref与out修饰符。 - 对于每个 argi 实参(i 大于 1),位于 argi 之前 v 的明确赋值状态与 argi-1 后 v 的状态是一致的。
 - 如果变量 v 以输出参数(形如 
out v)的方式传入实参,则expr后 v 的状态是明确赋值的。否则的话,expr后 v 的状态与 argn 后 v 的状态一致。 - 对于数组初始化器(array initializers,第七章第 6.10.4 节)、对象初始化器(object initializers,第七章第 6.10.2 节)、集合初始化器(collection initializers,第七章第 6.10.3 节)以及匿名对象初始化器(anonymous object initializers,第七章第 6.10.6 节),它们的明确赋值状态取决于定义这些构造所依据的扩展所决定。
 
简单赋值表达式
对于形如 w = expr-rhs 的表达式 expr:
- 在 
expr-rhs之前的 v 的明确赋值状态与在expr前 v 的明确赋值状态是一样的。 - 如果 w 是与 v 是同一变量,则 
expr之后的 v 的明确赋值状态为「已被明确扶植」。否则expr之后的 v 的明确赋值状态与expr-rhs之后的 v 的明确赋值状态是一样的。 
&& 表达式
对于形如 expr-first && expr-second 的表达式 expr:
- 在 
expr-first之前的 v 的明确赋值状态与在expr之前的 v 的明确赋值状态是一样的。 - 如果 
expr-first之后的 v 是明确赋值的或「在 true 表达式之后明确赋值」,那么在expr-second之前的 v 的明确赋值状态是明确赋值的。否则,它就不是明确赋值的。 - 在 
expr之后的 v 的明确赋值状态取决于:- 如果 
expr-first是值为 false 的常量表达式,则expr之后的 v 的明确赋值状态与在expr-first之后的 v 的状态是一样的。 - 不然的话,如果 
expr-first之后的 v 的状态是明确赋值的,那么expr之后的 v 的状态也是明确赋值的。 - 再不然,如果 
expt-second之后的 v 的状态是明确赋值的、并且expr-first之后的状态是「在 false 表达式之后明确赋值」,那么expr之后的 v 的状态是已明确赋值的。 - 否则,如果 
expr-second后的 v 的状态是明确赋值或「在 true 表达式之后明确赋值」,那么expr之后 v 的状态是「在 true 表达式之后明确赋值」。 - 再不然,如果 
expr-first后 v 的状态是「在 false 表达式之后明确赋值」且expr-second后 v 的状态是「在 false 表达式之后明确赋值」,那么expr的 v 的状态是「在 false 表达式之后明确赋值」。 - 否则,
expr之后的 v 的状态就是未明确赋值了。 
 - 如果 
 
在下例中
class A
{
    static void F(int x, int y) {
        int i;
        if (x >= 0 && (i = y) >= 0) {
            // i 明确赋值
        }
        else {
            // i 没有明确赋值
        }
        // i 没有明确赋值
    }
}
变量 i 在 if 语句的其中一个嵌入语句中被视作是明确赋值的,但在另一个则不是。在方法 F 的 if 语句中,变量 i 在第一个嵌入语句中被明确赋值,因为在表达式 (i = y) 执行的时间先于这段嵌入语句的执行时间。相反,变量 i 在第二个嵌入语句中是未明确赋值的,因为 x >= 0 为 false 时的结果是变量 i 未被赋值。
|| 表达式
对于形如 expr-first || expr-second 的表达式 expr:
- 在 
expr-first前 v 的明确赋值状态与expr前 v 的状态是一样的。 - 如果 
expr-first后 v 的状态是明确赋值或「在 false 表达式之后明确赋值」,那么在expr-second前 v 的状态是已明确赋值的。否则的话,他就是未明确赋值的。 - 在 
expr后 v 的明确赋值状态取决于:- 如果 
expr-first是值为 true 的常量表达式,则expr之后的 v 的明确赋值状态与在expr-first之后的 v 的状态是一样的。 - 不然的话,如果 
expr-first之后的 v 的状态是明确赋值的,那么expr之后的 v 的状态也是明确赋值的。 - 再不然,如果 
expt-second之后的 v 的状态是明确赋值的、并且expr-first之后的状态是「在 true 表达式之后明确赋值」,那么expr之后的 v 的状态是已明确赋值的。 - 否则,如果 
expr-second后的 v 的状态是明确赋值或「在 false 表达式之后明确赋值」,那么expr之后 v 的状态是「在 false 表达式之后明确赋值」。 - 再不然,如果 
expr-first后 v 的状态是「在 true 表达式之后明确赋值」且expr-second后 v 的状态是「在 true 表达式之后明确赋值」,那么expr的 v 的状态是「在 true 表达式之后明确赋值」。 - 否则,
expr之后的 v 的状态就是未明确赋值了。 
 - 如果 
 
在下例中
class A
{
    static void G(int x, int y) {
        int i;
        if (x >= 0 || (i = y) >= 0) {
            // i 没有明确赋值
        }
        else {
            // i 明确赋值
        }
        // i 没有明确赋值
    }
}
变量 i 在其中一个嵌入语句中被视作明确赋值,而在另一个中则不是。在方法 G 的 if 语句中,变量 i 在第二个嵌入语句中被视作明确赋值,因为表达式 (i = y) 的执行将先于这段嵌入语句。与此相反,变量 i 在第一个嵌入语句中被视作未明确赋值,因为 x >= 0 可能为 true,那么作为其结果变量 i 没有被赋值。
! 表达式
对于形如 ! expr-operand 的表达式 expr:
- 在 
expr-operand前 v 的明确赋值状态与expr前 v 的状态是一样的。 - 在 
expr后 v 的明确赋值状态取决于:- 如果 
expr-operand后 v 的状态是已明确赋值的,那么expr后 v 的状态是已明确赋值的 - 如果 
expr-operand后 v 的状态是未明确赋值的,那么expr后 v 的状态是未明确赋值的 - 如果 
expr-operand后 v 的状态是「在 false 表达式后明确赋值」,那么expr后 v 的状态是「在 true 表达式后明确赋值」。 - 如果 
expr-operand后 v 的状态是「在 true 表达式后明确赋值」,那么expr后 v 的状态是「在 false 表达式后明确赋值」。 
 - 如果 
 
?? 表达式
对于形如 expr-first ?? expr-second 的表达式 expr:
- 在 
expr-first前 v 的明确赋值状态与expr前 v 的状态是一样的。 - 在 
expr-second前 v 的明确赋值状态与expr-first后 v 的状态是一样的。 - 在 
expr后 v 的明确赋值状态取决于:- 若 
expr-first是值为 null 的常量表达式(第七章第十九节),则expr后 v 的状态与expr-second后 v 的状态是一致的。 - 否则的话,
expr后 v 的状态与expr-first后 v 的明确赋值状态一致。 
 - 若 
 
?: 表达式
对于形如 expr-cond ? expr-true : expr-false 的表达式 expr:
- 在 
expr-cond前 v 的明确赋值状态与expr前 v 的状态是一样的。 - 当且仅当满足以下一条时,在 
expr-true前 v 的明确赋值状态是已明确赋值的:expr-cond是值为 false 的常量表达式。expr-cond后 v 的状态是以明确赋值或「在 true 表达式后明确赋值」。
 - 当且仅当满足以下一条时,在 
expr-false前 v 的明确赋值状态是已明确赋值的:expr-cond是值为 true 的常量表达式。expr-cond后 v 的状态是以明确赋值或「在 false 表达式后明确赋值」。(注:原文此处有笔误)
 - 在 
expr后 v 的明确赋值状态取决于:- 若 
expr-cond是值为 true 的常量表达式(第七章第十九节),则expr后 v 的状态与expr-true后 v 的状态一致。 - 不然,若 
expr-cond是值为 false 的常量表达式(第七章第十九节),则expr后 v 的状态与expr-false后 v 的状态一致。 - 再不然,若 
expr-true后 v 的状态是明确赋值,且expr-false后 v 的状态也是明确赋值的,则expr后 v 的状态同样是明确赋值的。 - 否则,
expr之后的 v 的状态就是未明确赋值了。 
 - 若 
 
匿名函数
对于具有主体(块或表达式)的 Lambda 表达式(lambda-expression)或匿名方法表达式(anonymous-method-expression)expr 的主体(body):
- 在 
body之前的外部变量v的明确赋值状态与expr之前的v的状态是一样的。也就是说,外部变量的明确赋值状态继承自匿名函数上下文。 - 在 
expr之后的外部变量v的明确赋值状态与expr之前的v的状态是一样的。 
举例
delegate bool Filter(int i);
void F() {
    int max;
    // 错误,max 没有明确赋值
    Filter f = (int n) => n < max;
    max = 5;
    DoWork(f);
}
由于在匿名函数被声明的时候 max 没有明确赋值,所以这将产生一个「编译时错误」。举个例子。
delegate void D();
void F() {
    int n;
    D d = () => { n = 1; };
    d();
    //错误,n 没有明确赋值
    Console.WriteLine(n);
}
由于在匿名函数之外对匿名函数内部的 n 进行明确赋值是没有效果的,所以这同样会产生一个「编译时错误」。
变量引用
变量引用(variable-reference)是一个被归类到变量(variable)的表达式(expression)。变量引用表示一个存储空间,通过访问它可以获取当前值、保存新值。
variable-reference:
    expression
在 C 和 C++ 中,变量引用(variable-reference)中称为 lvalue。
变量引用的原子性
读写以下数据类型是原子性的(atomic):bool、 char、 byte、 sbyte、 short、 ushort、 uint、 int、 float 以及引用类型。另外,如果枚举的基础类型属于上述列表内的,则读写该枚举值也是原子性的。读写其它类型,包括 long、 ulong、 double、 decimal 以及用户自定义的类型时,不能保证一定是原子的。除专门为该墓地设计的库函数以外,对于增量(increment)或减量(decrement)这种情况,依旧不能保证原子性的读取、修改与写入(read-modify-write)。
[1]ARC:Automatic Reference Counting,自动引用计数,是开发程序时的一个编译级别的特性,用于自动内存管理。更多请访问此处以及此处。
__EOF__
C# Language Specification 5.0 翻译计划
C# Language Specification 5.0 (翻译)第五章 变量的更多相关文章
- C# Language Specification 5.0 (翻译)第一章 引言
		
C#(念作 See Sharp)是一种简单.现代.面向对象并且类型安全的编程语言.C# 源于 C 语言家族,因此 C.C++ 和 Java 工程师们能迅速上手.ECMA 国际[1](ECMA Inte ...
 - C# Language Specification 5.0 (翻译)第二章 词法结构
		
程序 C# 程序(program)由至少一个源文件(source files)组成,其正式称谓为编译单元(compilation units)[1].每个源文件都是有序的 Unicode 字符序列.源 ...
 - C# Language Specification 5.0 (翻译)第三章 基本概念
		
应用程序启动 拥有进入点(entry point)的程序集称应用程序(application).当运行一应用程序时,将创建一新应用程序域(application domain).同一个应用程序可在同一 ...
 - C# Language Specification 5.0 (翻译)第四章 类型
		
C# 语言的类型分为两大类:值类型(value type)和引用类型(reference type),而它们又都同时具有至少一个类型形参的泛型类型(generic type).类型形参(type pa ...
 - The P4 Language Specification v1.0.2 Header and Fields
		
前言 本文参考P4.org网站给出的<The P4 Language Specification v1.0.2>的第二部分首部及字段,仅供学习:). 欢迎交流! Header and Fi ...
 - C# Language Specification 5.0 (翻译)第六章 转换
		
转换使表达式可以当做一个明确的类型来加以处理.转换使得所给定类型的表达式以不同类型来处理,或使得没有某个类型的表达式获得该类型.转换可以是显式或隐式的,而这决定了是否需要显式地强制转换.比方说,从类型 ...
 - The P4 Language Specification v1.0.2 Introduction部分
		
Introduction P4 is a declarative language for expressing how packets are processed by the pipeline o ...
 - The P4 Language Specification v1.0.2 Parser
		
<p4规范>解析器部分详解 p4解析器是根据有限状态机的思想来设计的. 解析器中解析的过程可以被一个解析图(parser graph)所表示,解析图中所表示的某一个状态(或者说,在P4语言 ...
 - Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
		
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
 
随机推荐
- 使用MonkeyTest对Android客户端进行压力测试
			
目录 monkey命令简介 monkey命令参数说明 自动化实例 如何通过日志定位问题 1.monkey命令简介 Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中.它 ...
 - MySQL的binlog2sql闪回
			
从MySQL binlog解析出你要的SQL.根据不同选项,你可以得到原始SQL.回滚SQL.去除主键的INSERT SQL等. 用途=========== * 数据快速回滚(闪回)* 主从切换后新m ...
 - redis之禁用保护模式以及修改监听IP
			
今天在安装filebeat的时候,出现了关于redis报错的问题,所以来总结一下: 报错信息是: (error) DENIED Redis is running in protected mode b ...
 - OWASP TOP10(2017)
			
原文链接:https://www.t00ls.net/viewthread.php?from=notice&tid=39385
 - Alpha 任务状态总览(持续更新)
			
Alpha 任务状态总览(持续更新) Part 0 · 简 要 目 录 Part 1 · 流 程 Part 2 · 总 任 务 量 安 排 Part 3 · 爬 虫 任 务 Part 4 · 接 口 ...
 - Eclipse 中怎样自动格式化代码?
			
首先 有一个 检查代码风格的工具叫checkstyle,具体怎么下载,请自行百度.. 当你在eclipse安装好 checkstyle后,对于使用google标准的人来说,选择一个项目,右键,点击ch ...
 - VS2012中使用SOS调试CLR
			
之前看了<用WinDbg探索CLR世界>的一些列文章,发现SOS真的是一个非常好的调试.net的工具, 然后又惊喜的在http://blogs.msdn.com/b/marioheward ...
 - BZOJ2839:集合计数(容斥,组合数学)
			
Description 一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007. ...
 - 《阿里巴巴 Java 开发手册》划重点!
			
[强制]小数类型为 decimal,禁止使用 float 和 double. 说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果.如果存储的数 ...
 - 构造方法、 This关键字 、static、封装
			
1.1 构造方法 构造方法是一种特殊的方法,专门用于构造/实例化对象,形式: [修饰符] 类名(){ } 构造方法根据是否有参数分为无参构造和有参构. 1.1.1 无参构造 无参构造方法就是构造方法没 ...