C#变量50.docx
- 文档编号:10995075
- 上传时间:2023-05-28
- 格式:DOCX
- 页数:23
- 大小:24.07KB
C#变量50.docx
《C#变量50.docx》由会员分享,可在线阅读,更多相关《C#变量50.docx(23页珍藏版)》请在冰点文库上搜索。
C#变量50
变量81
5.1变量类别81
5.1.1静态变量81
5.1.2实例变量81
5.1.2.1类中的实例变量81
5.1.2.2结构中的实例变量82
5.1.3数组元素82
5.1.4值参数82
5.1.5引用参数82
5.1.6输出参数82
5.1.7局部变量83
5.2默认值83
5.3明确赋值84
5.3.1初始已赋值变量84
5.3.2初始未赋值变量85
5.3.3确定明确赋值的细则85
5.3.3.1一般语句规则85
5.3.3.2块语句、checked和unchecked语句86
5.3.3.3表达式语句86
5.3.3.4声明语句86
5.3.3.5if语句86
5.3.3.6switch语句87
5.3.3.7while语句87
5.3.3.8do语句87
5.3.3.9for语句87
5.3.3.10break、continue和goto语句88
5.3.3.11throw语句88
5.3.3.12return语句88
5.3.3.13try-catch语句88
5.3.3.14try-finally语句88
5.3.3.15try-catch-finally语句89
5.3.3.16foreach语句90
5.3.3.17using语句90
5.3.3.18lock语句90
5.3.3.19简单表达式的一般规则90
5.3.3.20带有嵌入表达式的表达式的一般规则91
5.3.3.21调用表达式和对象创建表达式91
5.3.3.22简单赋值表达式91
5.3.3.23&&表达式92
5.3.3.24||表达式92
5.3.3.25!
表达式93
5.3.3.26?
:
表达式93
5.4变量引用94
5.5变量引用的原子性94
变量
变量表示存储位置。
每个变量都具有一个类型,它确定哪些值可以存储在该变量中。
C#是一种类型安全的语言,C#编译器保证存储在变量中的值总是具有合适的类型。
通过赋值或使用++和--运算符可以更改变量的值。
在可以获取变量的值之前,变量必须已明确赋值(definitelyassigned)(第5.3节)。
如下面的章节所述,变量是初始已赋值(initiallyassigned)或初始未赋值(initiallyunassigned)。
初始已赋值的变量有一个正确定义了的初始值,并且总是被视为已明确赋值。
初始未赋值的变量没有初始值。
为了使初始未赋值的变量在某个位置被视为已明确赋值,变量赋值必须发生在通向该位置的每个可能的执行路径中。
变量类别
C#定义了7种变量类别:
静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量。
后面的章节将介绍其中的每一种类别。
在下面的示例中
classA
{
publicstaticintx;
inty;
voidF(int[]v,inta,refintb,outintc){
inti=1;
c=a+b++;
}
}
x是静态变量,y是实例变量,v[0]是数组元素,a是值参数,b是引用参数,c是输出参数,i是局部变量。
静态变量
用static修饰符声明的字段称为静态变量(staticvariable)。
静态变量在包含了它的那个类型的静态构造函数(第10.11节)执行之前就存在了,在关联的应用程序域终止时终止。
静态变量的初始值是该变量的类型的默认值(第5.2节)。
出于明确赋值检查的目的,静态变量被视为初始已赋值。
实例变量
未用static修饰符声明的字段称为实例变量(instancevariable)。
类中的实例变量
类的实例变量在创建该类的新实例时开始存在,在所有对该实例的引用都已终止,并且已执行了该实例的析构函数(若有)时终止。
类实例变量的初始值是该变量的类型的默认值(第5.2节)。
出于明确赋值检查的目的,类的实例变量被视为初始已赋值。
结构中的实例变量
结构的实例变量与它所属的结构变量具有完全相同的生存期。
换言之,当结构类型的变量开始存在或停止存在时,该结构的实例变量也随之存在或消失。
结构的实例变量与包含它的结构变量具有相同的初始赋值状态。
换言之,当结构变量本身被视为初始已赋值时,它的实例变量也被视为初始已赋值。
而当结构变量被视为初始未赋值时,它的实例变量同样被视为未赋值。
数组元素
数组的元素在创建数组实例时开始存在,在没有对该数组实例的引用时停止存在。
每个数组元素的初始值都是其数组元素类型的默认值(第5.2节)。
出于明确赋值检查的目的,数组元素被视为初始已赋值。
值参数
未用ref或out修饰符声明的参数为值参数(valueparameter)。
值形参在调用该参数所属的函数成员(方法、实例构造函数、访问器或运算符)时开始存在,并用调用中给定的实参的值初始化。
当返回该函数成员时值参数停止存在。
出于明确赋值检查的目的,值参数被视为初始已赋值。
引用参数
用ref修饰符声明的参数是引用参数(referenceparameter)。
引用参数不创建新的存储位置。
相反,引用参数表示的是那个在对该函数成员调用中被当作“自变量”的变量所表示的同一个存储位置。
因此,引用参数的值总是与基础变量相同。
下面的明确赋值规则适用于引用参数。
注意第5.1.6节中描述的输出参数的不同规则。
变量在可以作为引用参数在函数成员调用中传递之前,必须已明确赋值(第5.3节)。
在函数成员内部,引用参数被视为初始已赋值。
在结构类型的实例方法或实例访问器内部,this关键字的行为与该结构类型的引用参数完全相同(第7.5.7节)。
输出参数
用out修饰符声明的参数是输出参数(outputparameter)。
输出参数不创建新的存储位置。
相反,输出参数表示的是那个在对该函数成员调用中被当作“自变量”的变量所表示的同一个存储位置。
因此,输出参数的值总是与基础变量相同。
下面的明确赋值规则应用于输出参数。
注意第5.1.5节中描述的引用参数的不同规则。
变量在可以作为输出参数在函数成员调用中传递之前不一定要明确赋值。
在正常完成函数成员调用之后,每个作为输出参数传递的变量都被认为在该执行路径中已赋值。
在函数成员内部,输出参数被视为初始未赋值。
函数成员的每个输出参数在该函数成员正常返回前都必须已明确赋值(第5.3节)。
在结构类型的实例构造函数内部,this关键字的行为与结构类型的输出参数完全相同(第7.5.7节)。
局部变量
局部变量(localvariable)是通过local-variable-declaration来声明的,此声明可以出现在block、for-statement、switch-statement或using-statement中。
局部变量的生存期是程序执行过程中的某一“段”,在此期间,一定会为该局部变量保留存储。
此生存期从进入与它关联的block、for-statement、switch-statement或using-statement开始,一直延续到对应的block、for-statement、switch-statement或using-statement的执行以任何方式结束为止。
(进入封闭block或调用方法会挂起(但不会结束)当前的block、for-statement、switch-statement或using-statement的执行。
)如果以递归方式进入父block、for-statement、switch-statement或using-statement,则每次都创建局部变量的新实例,并且重新计算它的local-variable-initializer(如果有)。
局部变量不自动初始化,因此没有默认值。
出于明确赋值检查的目的,局部变量被视为初始未赋值。
local-variable-declaration可包括local-variable-initializer,在此情况下变量被视为在它的整个范围内(local-variable-initializer中提供的表达式内除外)已明确赋值。
在局部变量的范围内,在local-variable-declarator之前的文本位置引用该局部变量是编译时错误。
局部变量的实际生存期依赖于具体实现。
例如,编译器可能静态地确定块中的某个局部变量只用于该块的一小部分。
使用这种分析,编译器生成的代码可能会使该变量存储的生存期短于包含该变量的块的生存期。
局部引用变量所引用的存储的回收与该局部引用变量(第3.9节)的生存期无关。
foreach-statement和try-statement的specific-catch-clause也声明局部变量。
对于foreach-statement,局部变量是一个迭代变量(第8.8.4节)。
对于specific-catch-clause,局部变量是一个异常变量(第8.10节)。
foreach-statement或specific-catch-clause所声明的局部变量被视为在它的整个范围内已明确赋值。
默认值
以下类别的变量自动初始化为它们的默认值:
静态变量。
类实例的实例变量。
数组元素。
变量的默认值取决于该变量的类型,并按下面这样确定:
对于value-type的变量,默认值与该value-type的默认构造函数(第0章)所计算的值相同。
对于reference-type的变量,默认值为null。
初始化为默认值的实现方法一般是让内存管理器或垃圾回收器在分配内存以供使用之前,将内存初始化为“所有位归零”(all-bits-zero)。
由于这个原因,使用所有位归零来表示空(null)引用很方便。
明确赋值
在函数成员可执行代码中的给定位置,如果编译器可通过特定的静态流程分析(第5.3.3节)证明变量已自动初始化或已成为至少一个赋值的目标,则称该变量已明确赋值(definitelyassigned)。
非正式地讲,明确赋值的规则为:
初始已赋值的变量(第5.3.1节)总是被视为已明确赋值。
如果所有可能通向给定位置的执行路径都至少包含以下内容之一,则初始未赋值的变量(第
5.3.2节)被视为在该位置已明确赋值:
将变量作为左操作数的简单赋值(第7.13.1节)。
将变量作为输出参数传递的调用表达式(第7.5.5节)或对象创建表达式(第7.5.10.1节)。
对于局部变量,包含变量初始值设定项的局部变量声明(第8.5节)。
以上非正式规则所基于的正式规范在第5.3.1节、第5.3.2节和第5.3.3节中说明。
关于对一个struct-type变量的实例变量是否明确赋值,既可个别地也可作为整体进行跟踪。
除了上述规则,下面的规则也应用于struct-type变量及其实例变量:
如果一个实例变量的包含它的那个struct-type变量被视为已明确赋值,则该实例变量被视为已明确赋值。
如果一个struct-type变量的每个实例变量都被视为已明确赋值,则该结构类型变量被视为已明确赋值。
在下列上下文中要求实施明确赋值:
变量必须在获取其值的每个位置都已明确赋值。
这确保了从来不会出现未定义的值。
变量在表达式中出现被视为要获取该变量的值,除非当
该变量为简单赋值的左操作数,
该变量作为输出参数传递,或者
该变量为struct-type变量并作为成员访问的左操作数出现。
变量必须在它作为引用参数传递的每个位置都已明确赋值。
这确保了被调用的函数成员可以将引用参数视为初始已赋值。
函数成员的所有输出参数必须在函数成员返回的每个位置都已明确赋值,返回位置包括通过return语句实现的返回,或者通过执行语句到达函数成员体结尾的返回。
这确保了函数成员不在输出参数中返回未定义的值,从而使编译器能够把一个对函数成员的调用当作对某些变量的赋值,这些变量在该调用中被当作输出参数传递。
struct-type实例构造函数的this变量必须在该实例构造函数返回的每个位置明确赋值。
初始已赋值变量
以下类别的变量属于初始已赋值变量:
静态变量。
类实例的实例变量。
初始已赋值结构变量的实例变量。
数组元素。
值参数。
引用参数。
在catch子句或foreach语句中声明的变量。
初始未赋值变量
以下类别的变量属于初始未赋值变量:
初始未赋值结构变量的实例变量。
输出参数,包括结构实例构造函数的this变量。
局部变量,在catch子句或foreach语句中声明的那些除外。
确定明确赋值的细则
为了确定每个已使用变量都已明确赋值,编译器必须使用与本节中描述的进程等效的进程。
编译器处理每个具有一个或多个初始未赋值变量的函数成员的体。
对于每个初始未赋值的变量v,编译器在函数成员中的下列每个点上确定v的明确赋值状态(definiteassignmentstate):
在每个语句的开头处
在每个语句的结束点(第8.1节)
在每个将控制转移到另一个语句或语句结束点的arc上
在每个表达式的开头处
在每个表达式的结尾处
v的明确赋值状态可以是:
明确赋值。
这表明在能达到该点的所有可能的控制流上,v都已赋值。
未明确赋值。
当在bool类型表达式结尾处确定变量的状态时,未明确赋值的变量的状态可能(但不一定)属于下列子状态:
在true表达式后明确赋值。
此状态表明如果该布尔表达式计算为true,则v是明确赋值的,但如果布尔表达式计算为false,则不一定要赋值。
在false表达式后明确赋值。
此状态表明如果该布尔表达式计算为false,则v是明确赋值的,但如果布尔表达式计算为true,则不一定要赋值。
下列规则控制变量v的状态在每个位置是如何确定的。
一般语句规则
v在函数成员体的开头处不是明确赋值的。
v在任何无法访问的语句的开头处都是明确赋值的。
在任何其他语句开头处,为了确定v的明确赋值状态,请检查以该语句开头处为目标的所有控制流转移上的v的明确赋值状态。
当且仅当v在所有此类控制流转移上是明确赋值的时,v才在该语句的开始处明确赋值。
确定可能的控制流转移集的方法与检查语句可访问性的方法(第8.1节)相同。
在block、checked、unchecked、if、while、do、for、foreach、lock、using或switch等语句的结束点处,为了确定v的明确赋值状态,需检查以该语句结束点为目标的所有控制流转移上的v的明确赋值状态。
如果v在所有此类控制流转移上是明确赋值的,则v在该语句结束点明确赋值。
否则,v在语句结束点处不是明确赋值的。
确定可能的控制流转移集的方法与检查语句可访问性的方法(第8.1节)相同。
块语句、checked和unchecked语句
在指向位于某块中语句列表的第一个语句(如果语句列表为空,则指向该块的结束点)的控制转移上,v的明确赋值状态与块语句、checked或unchecked语句之前的v的明确赋值状态相同。
表达式语句
对于由表达式expr组成的表达式语句stmt:
v在expr的开头处与在stmt的开头处具有相同的明确赋值状态。
如果v在expr的结尾处明确赋值,则它在stmt的结束点也明确赋值;否则,它在stmt的结束点也不明确赋值。
声明语句
如果stmt是不带有初始值设定项的声明语句,则v在stmt的结束点与在stmt的开头处具有相同的明确赋值状态。
如果stmt是带有初始值设定项的声明语句,则确定v的明确赋值状态时可把stmt当作一个语句列表,其中每个带有初始值设定项的声明对应一个赋值语句(按声明的顺序)。
if语句
对于具有以下形式的if语句stmt:
if(expr)then-stmtelseelse-stmt
v在expr的开头处与在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的控制流转移上不是明确赋值的。
此后,当且仅当它在then-stmt的结束点是明确赋值的时,它在stmt的结束点才是明确赋值的。
否则,认为v在指向then-stmt或else-stmt,或指向stmt的结束点(如果没有else子句)的控制流转移上都不是明确赋值的。
switch语句
在带有控制表达式expr的switch语句stmt中:
位于expr开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
在指向可访问的switch块语句列表的控制流转移上,v的明确赋值状态就是它在expr结尾处的明确赋值状态。
while语句
对于具有以下形式的while语句stmt:
while(expr)while-body
v在expr的开头处与在stmt的开头处具有相同的明确赋值状态。
如果v在expr的结尾处明确赋值,则它在指向while-body和指向stmt结束点的控制流转移上是明确赋值的。
如果v在expr的结尾处具有“在true表达式后明确赋值”状态,则它在指向while-body的控制流转移上是明确赋值的,但在stmt的结束点处不是明确赋值的。
如果v在expr的结尾处具有“在false表达式后明确赋值”状态,则它在指向stmt的结束点的控制流转移上是明确赋值的,但在指向while-body的控制流转移上不是明确赋值的。
do语句
对于具有以下形式的do语句stmt:
dodo-bodywhile(expr);
v在从stmt的开头处到do-body的控制流转移上的明确赋值状态与在stmt的开头处的状态相同。
v在expr的开头处与在do-body的结束点具有相同的明确赋值状态。
如果v在expr的结尾处是明确赋值的,则它在指向stmt的结束点的控制流转移上是明确赋值的。
如果v在expr的结尾处的状态为“在false表达式后明确赋值”,则它在指向stmt的结束点的控制流转移上是明确赋值的。
for语句
对具有以下形式的for语句进行的明确赋值检查:
for(for-initializer;for-condition;for-iterator)embedded-statement
就如执行下列语句一样:
{
for-initializer;
while(for-condition){
embedded-statement;
for-iterator;
}
}
如果for语句中省略了for-condition,则在确定关于明确赋值的状态时,可把上述展开语句列表中的for-condition当作true。
break、continue和goto语句
由break、continue或goto语句引起的控制流转移上的v的明确赋值状态与它在该语句开头处的明确赋值状态是一样的。
throw语句
对于具有以下形式的语句stmt
throwexpr;
位于expr开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
return语句
对于具有以下形式的语句stmt
returnexpr;
位于expr开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
如果v是输出参数,则它必须在下列两个位置之一被明确赋值:
在expr之后
在包含return语句的try-finally或try-catch-finally的finally块的结尾处。
对于具有以下形式的语句stmt:
return;
如果v是输出参数,则它必须在下列两个位置之一被明确赋值:
在stmt之前
在包含return语句的try-finally或try-catch-finally的finally块的结尾处。
try-catch语句
对于具有以下形式的语句stmt:
trytry-block
catch(...)catch-block-1
...
catch(...)catch-block-n
位于try-block开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
位于catch-block-i(对于所有的i)开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
当且仅当v在try-block和每个catch-block-i(每个i从1到n)的结束点明确赋值时,stmt结束点处的v的明确赋值状态才是明确赋值的。
try-finally语句
对于具有以下形式的try语句stmt:
trytry-blockfinallyfinally-block
位于try-block开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
位于finally-block开头处的v的明确赋值状态与位于stmt开头处的v的明确赋值状态相同。
当且仅当下列条件中至少有一个为真时,位于stmt结束点处的v的明确赋值状态才是明确赋值的:
v在try-block的结束点明确赋值
v在finally-block的结束点明确赋值
如果控制流转移(例如,goto语句)从try-block内开始,在try-block外结束,那么如果v在finally-block的结束点明确赋值,v也被认为在该控制流转移上明确赋值。
(这不是必要条件,如果v由于其他原因在该控制流转移上明确赋值,则它仍被视为明确赋值。
)
try-catch-finally语句
对具有以下形式的try-catch-finally语句进行的明确赋值分析:
trytry-block
catch(...)c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 变量 50