C# 变量类别,静态变量,实例变量,局部变量,赋值变量
变量
变量表示存储位置。 每个变量都有一个类型,用于确定可以在变量中存储的值。 C # 是一种类型安全的语言,c # 编译器保证变量中存储的值始终为适当的类型。 可以通过赋值或使用和运算符来更改变量的值
++
--
。
在获取变量的值之前,必须 明确 地 ( 明确赋值 ) 。
如以下各节中所述,变量为 初始赋值 或 _ 初始未赋值 *。 最初分配的变量具有定义完善的初始值,并始终被视为明确赋值。 初始未赋值的变量没有初始值。 对于在某个特定位置被视为明确赋值的初始未赋值的变量,对该变量的赋值必须出现在通向该位置的每个可能的执行路径中。
变量类别
C # 定义了七种类型的变量:静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量。 以下各节介绍了其中的每个类别。
示例中
C#复制
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
称为
实例变量
。
类中的实例变量
当创建该类的新实例时,该类的实例变量就会成为存在,当不存在对该实例和实例的析构函数(如果已执行任何) ()时,该类的实例将停止存在。
类的实例变量的初始值是 (默认值) 变量类型的 默认 值。
出于明确赋值检查的目的,类的实例变量被视为初始赋值。
结构中的实例变量
结构的实例变量与它所属的结构变量的生存期完全相同。 换而言之,当结构类型的变量变为存在或不再存在时,该结构的实例变量也是如此。
结构的实例变量的初始赋值状态与包含结构变量的初始赋值状态相同。 换言之,当结构变量被视为初始已赋值,因此它的实例变量也被视为未赋值时,它的实例变量会同样取消分配。
数组元素
当创建数组实例时,数组的元素便会存在,并且在没有对该数组实例的引用时停止存在。
数组中每个元素的初始值都是默认值 (数组元素类型) 默认值 。
出于明确赋值检查的目的,数组元素被视为初始已赋值。
值参数
不带或修饰符声明的参数
ref
out
是一个
值参数
。
值参数可在调用函数成员 (方法、实例构造函数、访问器或运算符) 或参数所属的匿名函数时存在,并使用调用中给定的参数的值进行初始化。 当函数成员或匿名函数返回时,值参数通常不存在。 但是,如果通过匿名函数捕获 value 参数 ( 匿名函数表达式 ) ,则它的生存期将至少延长到从该匿名函数创建的委托或表达式树符合垃圾回收的条件。
出于明确赋值检查的目的,值参数被视为初始已赋值。
引用参数
使用修饰符声明的参数
ref
是一个
引用参数
。
引用参数不会创建新的存储位置。 相反,引用参数与给定作为函数成员或匿名函数调用中的参数的变量表示相同的存储位置。 因此,引用参数的值始终与基础变量相同。
以下明确赋值规则适用于引用参数。 请注意 输出参数 中所述的输出参数的不同规则。
- 在函数成员或委托调用中将变量作为引用参数传递时,必须明确地 ( 明确赋值 ) 赋值。
- 在函数成员或匿名函数中,引用参数被视为初始赋值。
在结构类型的实例方法或实例访问器中,
this
关键字的行为与
此访问
) (结构类型的引用参数完全相同。
输出参数
使用修饰符声明的参数
out
是一个
输出参数
。
Output 参数不会创建新的存储位置。 相反,output 参数表示作为函数成员或委托调用中的参数提供的相同存储位置。 因此,output 参数的值始终与基础变量相同。
以下明确的赋值规则适用于 output 参数。 请注意 引用参数 中所述的引用参数的不同规则。
- 在函数成员或委托调用中将变量作为 output 参数传递之前,不需要明确赋值。
- 完成函数成员或委托调用的正常完成后,作为输出参数传递的每个变量都被视为在该执行路径中分配。
- 在函数成员或匿名函数中,output 参数被视为初始未赋值。
- 函数成员或匿名函数的每个输出参数都必须在函数成员或匿名函数正常返回之前 ( 明确赋值 ) 。
在结构类型的实例构造函数中,
this
关键字的行为与
此访问
) (结构类型的输出参数完全相同。
局部变量
* 本地变量 _ 由 _local_variable_declaration * 声明,后者可能出现在 块 、 for_statement 、 switch_statement 或 using_statement 中;或由 try_statement foreach_statement 或 specific_catch_clause 。
局部变量的生存期是程序执行的一部分,在该过程中,将保证存储的存储空间。 此生存期至少会将输入中的内容扩展到 块 、 for_statement 、 switch_statement 、 using_statement 、 foreach_statement 或与之关联的 specific_catch_clause ,直到该 块 的执行、 for_statement 、 switch_statement 、 using_statement 、 foreach_statement 或 specific_catch_clause 以任何方式结束。 (进入封闭的 块 或调用方法挂起,但不是结束、执行当前 块 、 for_statement 、 switch_statement 、 using_statement 、 foreach_statement 或 specific_catch_clause 。如果本地变量是由匿名函数捕获 ) 捕获的外部变量 (,则它的生存期至少会持续到从匿名函数创建的委托或表达式树以及引用捕获的变量的任何其他对象,可用于垃圾回收。
如果以递归方式进入父 块 、 for_statement 、 switch_statement 、 using_statement 、 foreach_statement 或 specific_catch_clause ,则每次都会创建一个新的局部变量,并且每次都会计算其 local_variable_initializer (如果有)。
Local_variable_declaration 引入的本地变量不会自动初始化,因此没有默认值。 出于明确赋值检查的目的,由 local_variable_declaration 引入的局部变量被视为初始未赋值。 Local_variable_declaration 可能包括 local_variable_initializer ,在这种情况下,仅在初始化表达式 ( 声明语句 ) 后,才将变量视为明确赋值。
在 local_variable_declaration 引入的本地变量的作用域内,在其 local_variable_declarator 之前的文本位置引用该局部变量是编译时错误。 如果局部变量声明为隐式 ( 局部变量声明 ) ,则在其 local_variable_declarator 内引用该变量也是错误的。
Foreach_statement 或 specific_catch_clause 所引入的局部变量在整个范围内被视为已明确赋值。
局部变量的实际生存期取决于实现。 例如,编译器可能会以静态方式确定块中的某个局部变量仅用于该块的一小部分。 使用此分析,编译器可以生成代码,使变量的存储具有比包含块更短的生存期。
局部引用变量所引用的存储将在本地引用变量的生存期内被回收,这 ( 自动内存管理 ) 。
默认值
以下类别的变量会自动初始化为其默认值:
- 静态变量。
- 类实例的实例变量。
- 数组元素。
变量的默认值取决于变量的类型,并按如下所示确定:
- 对于 value_type 的变量,默认值与 value_type 的默认构造函数 ( 默认 构造函数) 计算的值相同。
-
对于
reference_type
的变量,默认值为
null
。
默认值的初始化通常是通过使内存管理器或垃圾回收器将内存初始化为全部位零来完成的,然后再将其分配给使用。 出于此原因,可以方便地使用所有位数表示空引用。
明确赋值
在函数成员的可执行代码中的给定位置,如果编译器可以通过特定的静态流分析 ( 确定明确赋值) 的精确规则 ,则将变量称为 明确 赋值,该变量已自动初始化或已成为至少一个赋值的目标。 非正式地说,明确赋值的规则如下:
最初分配的变量 、 最初未分配的变量 和 用于确定明确赋值的精确规则 中介绍了上述非正式规则基础的正式规范。
单独跟踪 struct_type 变量的实例变量的明确赋值状态。 除了以上规则,以下规则适用于 struct_type 变量及其实例变量:
- 如果实例变量的包含 struct_type 变量被视为已明确赋值,则将其视为明确赋值。
- 如果将 struct_type 变量视为已明确赋值,则将其视为明确赋值。
在以下上下文中,明确赋值是必需的:
- 变量必须在获取其值的每个位置明确赋值。 这可确保永远不会出现未定义的值。 表达式中的变量的出现位置被视为获取该变量的值,但在
- 变量是简单赋值的左操作数,
- 变量作为 output 参数传递,或
- 变量是 struct_type 变量,作为成员访问的左操作数出现。
- 变量必须在作为引用参数传递的每个位置上进行明确赋值。 这可确保被调用的函数成员可以考虑最初分配的引用参数。
-
函数成员的所有输出参数都必须在函数成员通过语句返回 (的每个位置上进行明确赋值,
return
或通过执行到达函数成员主体的末尾) 。 这可确保函数成员不在输出参数中返回未定义的值,从而使编译器可以考虑使用变量作为输出参数的函数成员调用,该参数等效于对变量赋值的输出参数。 -
this
Struct_type 实例构造函数的变量在该实例构造函数返回的每个位置都必须明确赋值。
最初分配的变量
以下类别的变量被分类为初始分配:
- 静态变量。
- 类实例的实例变量。
- 最初分配的结构变量的实例变量。
- 数组元素。
- 值参数。
- 引用参数。
-
catch
子句或语句中声明的变量foreach
。
初始未赋值变量
以下类别的变量被分类为初始未赋值:
- 初始未分配的结构变量的实例变量。
-
输出参数,包括
this
结构实例构造函数的变量。 -
局部变量(子句或语句中声明的变量除外)
catch
foreach
。
确定明确赋值的精确规则
为了确定每个已使用的变量是否已明确赋值,编译器必须使用与本部分中所述等效的进程。
编译器处理每个具有一个或多个初始未赋值变量的函数成员的正文。 对于每个初始未赋值的变量 v ,编译器会在函数成员的以下各点确定 _v * 的 * 明确赋值状态 _:
- 在每个语句的开头
- 在终结点 (每个语句的 终结点和可访问 性)
- 在将控制转移到另一条语句或语句结束点的每个弧线上
- 在每个表达式的开头
- 在每个表达式的末尾
V 的明确赋值状态可以是:
- 明确赋值。 这表明,在所有可能的控制流到目前为止, v 已分配有一个值。
-
未明确赋值。 对于类型为的表达式末尾的变量状态
bool
,未明确赋值的变量的状态可能 (但不一定) 属于以下子状态之一: - 在 true 表达式后明确赋值。 此状态表明,如果布尔表达式的计算结果为 true,则会明确赋值 v ; 如果布尔表达式的计算结果为 false,则不一定赋值。
- 在 false 表达式后明确赋值。 此状态表明,如果布尔表达式的计算结果为 false,则会明确赋值 v ; 如果布尔表达式的计算结果为 true,则不一定赋值。
以下规则控制如何在每个位置确定变量 v 的状态。
语句的一般规则
- 在函数成员主体的开头, v 未明确赋值。
- 在任何无法访问的语句开始时,均明确赋值 v 。
- V 在任何其他语句开始时的明确赋值状态是通过检查 v 在该语句开头的所有控制流传输上的明确赋值状态。 如果 (并且仅当在所有此类控制流传输上都明确分配) v 时,则在语句开头明确赋值 v 。 可以按照与检查语句可访问性相同的方式确定一组可能的控制流传输 ( 终结点和可访问 性) 。
-
在块、、、、、、、、或语句的终结点上,
v
的明确赋值状态
checked
unchecked
if
while
do
for
foreach
lock
using
switch
是通过在所有控制流传输上检查 v 的明确赋值状态,该状态针对该语句的终结点。 如果对所有此类控制流传输明确赋值 v ,则 v 在语句的终结点上是明确赋值的。 本来在语句的结束点上不明确赋值 v 。 可以按照与检查语句可访问性相同的方式确定一组可能的控制流传输 ( 终结点和可访问 性) 。
Block 语句、checked 和 unchecked 语句
如果语句列表为空,则在将控件传输到块中语句列表的第一条语句时,将控件上的
v
的明确赋值状态传输到块的第一条语句,如果语句列表为空) ,则为与块、或语句前面的
v
的明确赋值语句 (
checked
unchecked
。
表达式语句
对于包含表达式 expr 的表达式语句 stmt :
- 在 stmt 开始时, v 在 expr 开头具有相同的明确赋值状态。
- 如果在 expr 的末尾指定了 " v ",则它在 stmt 的终结点上进行明确赋值;本来它不是在 stmt 终结点明确赋值的。
声明语句
- 如果 stmt 是没有初始值设定项的声明语句,则 v 在 stmt 的终结点上的明确赋值状态与 stmt 的开头相同。
- 如果 stmt 是带有初始值设定项的声明语句,则 v 的明确赋值状态被确定为: stmt 是语句列表,每个具有初始值设定项的声明的赋值语句) (。
If 语句
对于
if
窗体的语句
stmt
:
C#复制
if ( expr ) then_stmt else else_stmt
- 在 stmt 开始时, v 在 expr 开头具有相同的明确赋值状态。
- 如果在 expr 结束时, v 是明确赋值的,则在控制流传输到 then_stmt ,并将其分配到 else_stmt ,或者在没有 else 子句的情况下将其分配给 stmt 的终结点。
- 如果在 expr 的末尾, v 的状态为 "明确分配给 true 表达式",则它在控制流到 then_stmt 的传输中是明确赋值的,如果不存在 else 子句,则不会在控制流传输上明确分配到 else_stmt 或到 stmt 的终结点。
- 如果 v 在 expr 的末尾具有状态 "在假表达式后明确赋值",则它将在控制流传输上明确分配到 else_stmt ,而不是明确分配给 then_stmt 的控制流传输。 当且仅当在 then_stmt 的终结点明确赋值时,它才会在 stmt 的终结点上进行明确赋值。
- 否则,在控制流到 then_stmt 或 else_stmt 的控制流传输上, v 被视为未明确分配,如果不存在 else 子句,则被视为 stmt 的终结点。
Switch 语句
switch
使用控制表达式
expr
的语句
stmt
:
- Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的状态相同。
- 到可访问的 switch 块语句列表的控制流传输上, v 的明确分配状态与 expr 末尾的 v 的明确赋值状态相同。
While 语句
对于
while
窗体的语句
stmt
:
C#复制
while ( expr ) while_body
- 在 stmt 开始时, v 在 expr 开头具有相同的明确赋值状态。
- 如果在 expr 结束时, v 是明确赋值的,则它在控制流传输上明确分配到 while_body 和 stmt 的终结点。
- 如果在 expr 的末尾, v 的状态为 "明确分配给 true 表达式",则它在控制流传输上明确分配到 while_body ,而不是在 stmt 的终结点明确赋值。
- 如果 v 在 expr 的末尾具有状态 "在假表达式后明确赋值",则它在控制流到 stmt 的终点的控制流传输上明确赋值,但并没有明确地分配给 while_body 的控制流传输。
Do 语句
对于
do
窗体的语句
stmt
:
C#复制
do do_body while ( expr ) ;
- 在从 stmt 开始到 do_body 时, v 在控制流传输上具有相同的明确赋值状态,如 stmt 的开头。
- 在 expr 的开头, v 与 do_body 的终结点具有相同的明确赋值状态。
- 如果在 expr 结束时, v 是明确赋值的,则它在控制流到 stmt 终点的传输上明确赋值。
- 如果在 expr 的末尾, v 的状态为 "在假表达式后明确赋值",则它在控制流到 stmt 终点的传输上明确赋值。
For 语句
对以下形式的语句的明确赋值检查
for
:
C#复制
for ( for_initializer ; for_condition ; for_iterator ) embedded_statement
像编写语句一样完成:
C#复制
{
for_initializer ;
while ( for_condition ) {
embedded_statement ;
for_iterator ;
如果语句中省略了
for_condition
for
,则计算明确赋值将继续,就像上述扩展中的
for_condition
已替换为
true
。
Break、continue 和 goto 语句
由、或语句导致的控制流传输上的
v
的明确赋值状态与
break
continue
goto
语句开头的
v
的明确赋值状态相同。
Throw 语句
对于窗体的语句 stmt
C#复制
throw expr ;
Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的明确赋值状态相同。
Return 语句
对于窗体的语句 stmt
C#复制
return expr ;
- Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的明确赋值状态相同。
- 如果 v 是 output 参数,则必须为其赋值:
- expr 之后
-
或包含语句的块的结尾
finally
try
-finally
try
-catch
-finally
return
。
对于窗体的语句 stmt:
C#复制
return ;
- 如果 v 是 output 参数,则必须为其赋值:
- stmt 之前
-
或包含语句的块的结尾
finally
try
-finally
try
-catch
-finally
return
。
Try-catch 语句
对于窗体的语句 stmt :
C#复制
try try_block
catch(...) catch_block_1
catch(...) catch_block_n
- 在 try_block 开始时, v 的明确赋值状态与 stmt 开头的 v 的明确赋值状态相同。
- 任何 i ) catch_block_i (的开头的 v 的明确分配状态与 stmt 开头的 v 的明确赋值状态相同。
- 如果 (并且仅当) v 在 try_block 的终结点上明确分配,并且每个 catch_block_i (每个 ) 的 每个 ,则该终结点的终结点的明确赋值 状态将明确 赋值。
Try-catch 语句
对于
try
窗体的语句
stmt
:
C#复制
try try_block finally finally_block
- 在 try_block 开始时, v 的明确赋值状态与 stmt 开头的 v 的明确赋值状态相同。
- 在 finally_block 开始时, v 的明确赋值状态与 stmt 开头的 v 的明确赋值状态相同。
- 如果 (并且仅当) 至少满足以下条件之一时,就会明确分配 v 在 stmt 终结点上的明确分配状态:
- v 在 try_block 的终结点明确赋值
- v 在 finally_block 的终结点明确赋值
例如,如果控制流传输 (例如,
goto
语句) 在
try_block
内开始,并在
try_block
外结束,则在该控制流传输上,
v
也被视为明确分配,前提是在
finally_block
的终结点明确赋值
v
。 (这种情况并不是唯一的情况,如果对此控制流传输的另一个原因明确指定
v
,则仍将其视为明确赋值。 )
Try-catch-finally 语句
对
try
-
catch
-
finally
以下形式的语句的明确赋值分析:
C#复制
try try_block
catch(...) catch_block_1
catch(...) catch_block_n
finally *finally_block*
如语句是
try
-
finally
包含语句的语句一样完成
try
-
catch
:
C#复制
try {
try try_block
catch(...) catch_block_1
catch(...) catch_block_n
finally finally_block
下面的示例演示了
try
try 语句 (的
语句的不同块如何) 影响明确赋值。
C#复制
class A
static void F() {
int i, j;
try {
goto LABEL;
// neither i nor j definitely assigned
i = 1;
// i definitely assigned
catch {
// neither i nor j definitely assigned
i = 3;
// i definitely assigned
finally {
// neither i nor j definitely assigned
j = 5;
// j definitely assigned
// i and j definitely assigned
LABEL:;
// j definitely assigned
Foreach 语句
对于
foreach
窗体的语句
stmt
:
C#复制
foreach ( type identifier in expr ) embedded_statement
- Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的状态相同。
- 控制流传输到 embedded_statement 或 stmt 终结点上的 v 的明确分配状态与 expr 末尾处的 v 状态相同。
使用语句
对于
using
窗体的语句
stmt
:
C#复制
using ( resource_acquisition ) embedded_statement
- 在 resource_acquisition 开始时, v 的明确赋值状态与 stmt 开头的 v 状态相同。
- 控制流传输到 embedded_statement 的 v 的明确分配状态与 resource_acquisition 末尾处的 v 状态相同。
Lock 语句
对于
lock
窗体的语句
stmt
:
C#复制
lock ( expr ) embedded_statement
- Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的状态相同。
- 控制流传输到 embedded_statement 的 v 的明确分配状态与 expr 末尾处的 v 状态相同。
Yield 语句
对于
yield return
窗体的语句
stmt
:
C#复制
yield return expr ;
- Expr 开头的 v 的明确赋值状态与 stmt 开头的 v 的状态相同。
- 在 stmt 结束时, v 的明确赋值状态与 expr 末尾的 v 状态相同。
-
yield break
语句对明确赋值状态没有影响。
简单表达式的一般规则
以下规则适用于这些类型的表达式:文本 (
文本
) ,简单名称 (
简单名称
) ,成员访问表达式 (
成员访问
) ,非索引的基本访问表达式 (
基本访问
) ,
typeof
表达式 (
typeof 运算符
) ,默认值表达式 (默认值
表达式
) 和 (
nameof
Nameof 表达式
) 默认值表达式。
- 此类表达式结束时, v 的明确赋值状态与表达式开头 v 的明确赋值状态相同。
带有嵌入式表达式的表达式的一般规则
以下规则适用于这些类型的表达式:带圆括号的表达式 (
带圆括号
的表达式) ,元素访问表达式 (
元素访问
) ,使用索引编制索引的基本访问表达式 (
基本访问
) 、递增和递减表达式 (
后缀增量和减量运算符
、
前缀增量和减量运算符
) 、转换表达式 (
强制转换表达式
) 一元、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、)
+
-
~
*
+
-
*
/
%
<<
>>
<
<=
>
>=
==
!=
is
as
&
|
^
、复合赋值表达式 (复合
赋值)
和
checked
表达式 (new
unchecked
运算符)
(、数组和委托创建表达式) 。 (
其中每个表达式都具有一个或多个按固定顺序无条件计算的子表达式。 例如,二元
%
运算符计算运算符的左侧,然后计算右侧。 索引操作将计算索引表达式的值,然后按从左至右的顺序计算每个索引表达式。 对于具有子表达式
e1、e2、...、eN
的表达式
expr
,按以下顺序计算:
- 在 e1 开始时, v 的明确赋值状态与 expr 开头的明确赋值状态相同。
- 在 ei 开始时, v 的明确赋值状态 ( i 大于一个) 与上一个子表达式末尾的明确赋值状态相同。
- 在 expr 结束时, v 的明确赋值状态与 eN 末尾的明确赋值状态相同
调用表达式和对象创建表达式
对于以下形式的调用表达式 expr :
C#复制
primary_expression ( arg1 , arg2 , ... , argN )
或形式的对象创建表达式:
C#复制
new type ( arg1 , arg2 , ... , argN )
- 对于调用表达式,v primary_expression 之前的 v 的明确赋值状态与 expr 前面的 v 的状态相同。
- 对于调用表达式,v 之前的 v 的明确赋值状态 与 primary_expression 后 v 的状态相同。
- 对于对象创建表达式,v 之前的 v 的明确赋值状态 与 expr 前面的 v 的状态相同。
-
对于每个参数
argi
,
argi
之后的
v
的明确赋值状态由标准表达式规则确定,忽略任何
ref
或out
修饰符。 - 对于每 个大于一 的参数 argi , argi 之前的 v 的明确赋值状态与上一个 arg 后 v 的状态相同。
-
如果变量
v
作为
out
参数传递 (例如,形式的参数out v
) 在任何参数中,则 expr 后 v 的状态将明确赋值。 本来 expr 后 v 的状态与 argN 后 v 的状态相同。 - 对于数组初始值设定项 ( 数组创建表达式 ) ,对象初始值设定项 ( 对象初始值设定 项) ,集合初始值设定项 ( 集合 初始值设定项) 和匿名对象初始值设定项 ( 匿名对象创建表达式 ) ,明确的赋值状态由定义这些构造的扩展确定。
简单赋值表达式
对于以下形式
的表达式表达式
w = expr_rhs
:
- 在 expr_rhs 之前, v 的明确赋值状态与 expr 之前的 v 的明确赋值状态相同。
- Expr 后 v 的明确赋值状态由确定:
- 如果 w 是与 v 相同的变量,则 expr 后 v 的明确赋值状态是明确赋值的。
- 否则,如果在结构类型的实例构造函数中发生分配,则如果 w 是属性访问,该属性访问在正在构造的实例上指定了自动实现的属性 P ,而 v 是 P 的隐藏支持字段,则 expr 后 v 的明确赋值状态为明确赋值。
- 否则,在 expr 后, v 的明确赋值状态与 expr_rhs 后 v 的明确分配状态相同。
&& (条件和) 表达式
对于以下形式
的表达式表达式
expr_first && expr_second
:
- 在 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 的状态将明确赋值。
- 否则,如果在 expr_second 后 ,v 的状态是明确赋值的,并且 expr_first 后 v 的状态是 "在 false 表达式后明确赋值",则 expr 后 v 的状态将明确赋值。
- 否则,如果在 expr_second 后 v 的状态是明确赋值的或 "在 true 表达式后明确赋值",则 expr 后 v 的状态为 "在 true 表达式后明确赋值"。
- 否则,如果 " v " expr_first 后面的 "v" 的状态为 "在假表达式后明确赋值",并且在 expr_second 后 v 的状态为 "在假表达式后明确赋值",则 expr 后 v 的状态为 "在假表达式后明确赋值"。
- 否则,不明确赋值 expr 后的 v 状态。
示例中
C#复制
class A
static void F(int x, int y) {
int i;
if (x >= 0 && (i = y) >= 0) {
// i definitely assigned
else {
// i not definitely assigned
// i not definitely assigned
在
i
语句的一个嵌入语句中(
if
而不是在另一个语句中),该变量被视为已明确赋值。 在
if
方法的语句中
F
,变量在
i
第一个嵌入语句中是明确赋值的,因为在执行
(i = y)
此嵌入语句之前,表达式的执行始终为。 与此相反,
i
第二个嵌入语句中的变量并不是明确赋值的,因为
x >= 0
可能已对 false 进行了测试,导致变量
i
未赋值。
|| (条件表达式或) 表达式
对于以下形式
的表达式表达式
expr_first || expr_second
:
- 在 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 的状态将明确赋值。
- 否则,如果在 expr_second 后 ,v 的状态是明确赋值的,并且 expr_first 后 v 的状态是 "在 true 表达式后明确赋值",则 expr 后 v 的状态将明确赋值。
- 否则,如果在 expr_second 后 v 的状态是明确赋值的或 "在假表达式后明确赋值",则 expr 后 v 的状态为 "在假表达式后明确赋值"。
- 否则,如果在 expr_first 后 的 v 状态是 "在 true 表达式后明确赋值",并且在 expr_second 后 v 的状态为 "在 true 表达式后明确赋值",则 expr 后 v 的状态为 "在 true 表达式后明确赋值"。
- 否则,不明确赋值 expr 后的 v 状态。
示例中
C#复制
class A
static void G(int x, int y) {
int i;
if (x >= 0 || (i = y) >= 0) {
// i not definitely assigned
else {
// i definitely assigned
// i not definitely assigned
在
i
语句的一个嵌入语句中(
if
而不是在另一个语句中),该变量被视为已明确赋值。 在
if
方法的语句中
G
,变量在
i
第二个嵌入语句中是明确赋值的,因为在执行
(i = y)
此嵌入语句之前,表达式的执行始终为。 与此相反,
i
第一个嵌入语句中的变量不是明确赋值的,因为
x >= 0
可能测试了 true,导致变量
i
被分配。
! (逻辑求反) 表达式
对于以下形式
的表达式表达式
! expr_operand
:
- 在 expr_operand 之前, v 的明确赋值状态与 expr 之前的 v 的明确赋值状态相同。
- Expr 后 v 的明确赋值状态由确定:
- 如果在 * expr_operand * 之后的 v 状态为明确赋值,则 expr 后 v 的状态将明确赋值。
- 如果未明确赋值 * expr_operand * 之后的 v 状态,则不明确赋值 expr 后的 v 状态。
- 如果在 * expr_operand * 之后的 v 状态是 "在假表达式后明确赋值",则 expr 后 v 的状态为 "在 true 表达式后明确赋值"。
- 如果在 * expr_operand * 之后的 v 状态是 "在 true 表达式后明确赋值",则 expr 后 v 的状态为 "在假表达式后明确赋值"。
?? ) 表达式 (空合并
对于以下形式
的表达式表达式
expr_first ?? expr_second
:
- 在 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_cond 之前, v 的明确赋值状态与 expr 前面的 v 的状态相同。
- 当且仅当以下其中一项保留时,v expr_true 前 v 的明确赋值状态是明确赋值的:
-
expr_cond
是具有值的常量表达式
false
- expr_cond 的状态 为明确 赋值或 "true expression 之后明确赋值"。
- 当且仅当以下其中一项保留时,v expr_false 前 v 的明确赋值状态是明确赋值的:
-
expr_cond
是具有值的常量表达式
true
- expr_cond 的状态为明确赋值或 "在 false 表达式后明确赋值"。
- Expr 后 v 的明确赋值状态由确定:
匿名函数
对于 lambda_expression 或带有正文的 anonymous_method_expression expr ( 块 或 表达式 ) 体 :
- 在 body 之前,外部变量 v 的明确赋值状态与 expr 前面的 v 的状态相同。 也就是说,外部变量的明确赋值状态是从匿名函数的上下文继承而来的。
- Expr 后面的外部变量 v 的明确赋值状态与 expr 前面的 v 的状态相同。
示例
C#复制
delegate bool Filter(int i);
void F() {
int max;
// Error, max is not definitely assigned
Filter f = (int n) => n < max;
max = 5;
DoWork(f);
生成编译时错误,因为在
max
声明匿名函数的位置未明确赋值。 示例
C#复制
delegate void D();
void F() {
int n;
D d = () => { n = 1; };
d();
// Error, n is not definitely assigned