变量(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 节中所描述的规则不同。
在结构类型的实例方法中,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)独立于该局部引用变量(第三章第九节)的生命周期
下列分类的变量将自动初始化其默认值:
变量默认值依赖于其变量类型,并由以下规定确定:
默认值初始化是一般是内存管理器(memory manager)或垃圾回收器(garbage collector)在分配内存给其使用之前,将内存的「所有位置零(all-bits-zero)」。基于此,很方便使用「所有位置零」表示空引用(null reference)。
在函数成员可执行代码中的给定位置,如果编译器能够通过特别的静态流程分析(particular static flow analysis,第五章第 3.3 节)证明变量已被自动初始化(automatically initialized)或至少一次被作为赋值的目标,那么称变量已被明确赋值。非正式地来讲,明确赋值的规则如下:
对于以上这些非正式的规则的正式规范在第五章第 3.1 节、第五章第 3.2 节以及第五章第 3.3 节中介绍。
对于 struct-type
变量之实例变量的明确赋值状态,既可以单独跟踪(tracked individually),也可以整体跟踪(tracked collectively)。另外除上述规则外,下列规则也将应用于 struct-type
变量及其实例变量:
struct-type
变量时,被视作已被明确赋值;struct-type
变量的每个实例变量都视作明确赋值时,它也被视作明确赋值。下列上下文是必须明确赋值的:
struct-type
变量且作为成员访问的左操作数出现。return
语句或通过执行到函数成员体的尾部并返回),以确保函数成员不会在输出参数中返回未定义的值(undefined values),因此允许编译器把将变量作为输出参数的函数成员的调用视作等价于对变量的赋值。struct-type
实例构造函数的 this
变量必须在实例构造函数所返回的每一个位置上明确赋值。以下变量分类被归入初始已赋值(initially assigned):
以下变量分类被归入初始未赋值(initially unassigned):
this
变量为了确定每个所用变量是否已明确赋值,编译器必须使用与本节所述之一等价的处理过程。
编译器会处理每一个具有至少一个初始未赋值变量的函数成员的主体。对于每一个初始未赋值的变量 v
,编译器在以下函数成员内节点确定其明确赋值状态:
v
的明确赋值状态可以是:
v
都已经赋值。v
将明确赋值,但当其运算结果为 false 时则不一定。v
将明确赋值,但当其运算结果为 true 时则不一定。下列规则控制变量 v
在每一个位置上如何决定明确赋值状态的。
在指向某块(block)语句列表(statement list)中第一句语句(如果语句列表为空(empty)则指向该块结束点)的控制转移上 v 的明确赋值状态与语句、checked 或 unchecked 语句之前的 v 的明确赋值状态一致。
对于由表达式 expr
组成的表达式语句 stmt
:
expr
开头位置的 v 的明确赋值状态与 stmt
开头处的一致;expr
结尾处是明确赋值的,那么在 stmt
结尾点也是明确赋值的,不然相反。stmt
是不带初始化器的声明语句,则 v 在 stmt
结束点与在 stmt
开头处具有相同的明确赋值状态;stmt
是一个带初始化器的声明语句,则确定 v 的明确赋值状态时可将之视为语句列表,每个带初始化器的声明对应一句赋值语句(按声明的顺序)。对于形如以下的 if 语句 stmt
:
if ( expr ) then-stmt else else-stmt
expr
开头位置的 v 的明确赋值状态与 stmt
开头处一致;expr
结尾处是明确赋值的,那么在指向 then-stmt
以及任意一个 else-stmt
或 stmt
结尾点(如果没有 else 子句)的控制流转移上是明确赋值的;expr
结尾处的状态是「在 true 表达式之后明确赋值」,那么在指向 then-stmt
的控制流转移上是明确赋值的,但指向 else-stmt
或 stmt
的结尾点(如果没有 else 子句)的控制流转移上是未明确赋值的;expr
结尾点的状态是「在 false 表达式之后明确赋值」,那么在指向 else-stmt
的控制流转移上是明确赋值的,但指向 then-stmt
的控制流转移上是未明确赋值的。位于 stmt
结束点(当且仅当在 then-stmt
结尾点是明确赋值的)上是明确赋值的。then-stmt
、else-stmt
或 stmt
结尾点(如果没有 else 子句)的控制流转移上的 v 被视作未明确赋值。在带有控制表达式 expr
的 switch 语句 stmt
:
expr
开始位置的 v 的明确赋值状态与位于 stmt
开头位置的 v 的状态是一致的;expr
结尾处的明确赋值状态。对于形如以下的 while 语句 stmt
:
while ( expr ) while-body
expr
开头位置的 v 的明确赋值状态与 stmt
开头处的一致;expr
结尾处是明确赋值的,那么在指向 while-body
的控制流转移上以及在 stmt
结尾点上的状态是明确赋值的;expr
结尾点的状态是「在 true 表达式之后明确赋值」,那么在指向 while-body
的控制流转移上它是明确赋值的,但在 stmt
结尾点上是未明确赋值的;expr
结尾点的状态是「在 false 表达式之后明确赋值」,那么在指向 stmt
结尾点的控制流转移上它是明确赋值的,单位指向 while-body
的控制流转移上它未明确赋值的。对 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-initializer ; for-condition ; for-iterator ) embedded-statement
如下面所写语句一样:
{
for-initializer ;
while ( for-condition ) {
embedded-statement ;
for-iterator ;
}
}
如果 for-condition
从语句中省略,则当评估明确赋值状态时,可将上述展开语句中的 for-condition
当做 true
。
由 break
、continue
或 goto
语句导致的控制流转移(control flow transfer)上 v 的明确赋值状态与语句开始处 v 的明确赋值状态一致。
对于以下形式的语句 stmt
:
throw expr ;
位于 expr
开头处 v 的明确赋值状态与 stmt
开头处 v 的状态一致。
对于以下形式的语句 stmt
:
return expr ;
expr
开头 v 的明确赋值状态与 stmt
开头 v 的状态是一致的。expr
之后;try-finally
或 try-catch-finally
的 finally
块的结尾处。对于以下形式的语句 stmt
:
return ;
stmt
之前;return
语句的 try-catch
或 try-catch-finally
的 finally
块的结尾处。对于以下形式的语句 stmt
:
try try-block
catch(...) catch-block-1
...
catch(...) catch-block-n
try-block
开始处的 v 的明确赋值状态与 stmt
开头处一致;catch-block-i
开始处(对于任意 i)的 v 的明确赋值状态与 stmt
开头处一致;try-block
结尾点和每一个 catch-block-i
(i ∈ [1, n])的结尾点是明确赋值的,则位于 stmt
结尾点的 v 的明确赋值状态是明确赋值的。对于以下形式的 try
语句 stmt
:
try try-block finally finally-block
try-block
开始处的 v 的明确赋值状态与 stmt
开头处一致;finally-block
开始处的 v 的明确赋值状态与 stmt
开头处一致;stmt
结尾点的 v 的明确赋值状态是明确赋值的:
try-block
结束点是明确赋值的;finally-bolck
结束点是明确赋值的。如果控制流转移(比方说,一个 goto
语句)从 try-block
内开始,结束于 try-block
外,那么如果 v 在 finally-block
的结束点上明确赋值,在控制流转移上 v 同样被视作明确赋值的(这不是必要条件,如果 v 在 finally-block
结束点上是明确赋值的,那么它任被视作明确赋值)。
对于形如下面这段 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
语句 stmt
:
foreach ( type identifier in expr ) embedded-statement
expr
开头处的 v 的明确赋值状态与 stmt
开头处的一致;embedded-statement
的控制流转移上的 v 的明确赋值状态与 expr
结尾处的一致。对于以下形式的 using
语句 stmt
:
using ( resource-acquisition ) embedded-statement
resource-acquisition
开头处的 v 的明确赋值状态与 stmt
开头处的一致;embedded-statement
的控制流转移上的 v 的明确赋值状态与 resource-acquisition
结尾处的一致。对于以下形式的 lock
语句 stmt
:
lock ( expr ) embedded-statement
expr
开头处的 v 的明确赋值状态与 stmt
开头处的一致;embedded-statement
的控制流转移上的 v 的明确赋值状态与 expr
结尾处的一致。对于以下形式的 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 节)。
以下规则可应用于下诸类型之表达式:带括号的表达式(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
,按下列顺序执行:
expr
开头的状态一致;对于以下形式的调用表达式(invocation expression)expr
:
primary-expression ( arg1 , arg2 , … , argn )
或以下形式的对象创建表达式(object creation expression):
new type ( arg1 , arg2 , … , argn )
primary-expression
前 v 的明确赋值状态与 expr
前 v 的状态一致。primary-expression
后 v 的状态是一致的。expr
后 v 的状态是一致的。ref
与 out
修饰符。out v
)的方式传入实参,则 expr
后 v 的状态是明确赋值的。否则的话,expr
后 v 的状态与 argn 后 v 的状态一致。对于形如 w = expr-rhs
的表达式 expr
:
expr-rhs
之前的 v 的明确赋值状态与在 expr
前 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 翻译计划