您好,欢迎访问三七文档
5.变量变量表示存储位置。每个变量都具有一个类型,它确定哪些值可以存储在该变量中。C#是一种类型安全的语言,C#编译器保证存储在变量中的值总是具有合适的类型。通过赋值或使用++和--运算符可以更改变量的值。在可以获取变量的值之前,变量必须已明确赋值(第5.3节)。如下面的章节所述,变量是初始已赋值或初始未赋值。初始已赋值的变量有一个正确定义了的初始值,并且总是被视为已明确赋值。初始未赋值的变量没有初始值。为了使初始未赋值的变量在某个位置被视为已明确赋值,变量赋值必须发生在通向该位置的每个可能的执行路径中。5.1变量类别C#定义了七种变量类别:静态变量、实例变量、数组元素、值参数、引用参数、输出参数以及局部变量。后面的章节将介绍其中的每一种类别。在下面的示例中,classA{publicstaticintx;inty;voidF(int[]v,inta,refintb,outintc){inti=1;c=a+b++;}}x是静态变量,y是实例变量,v[0]是数组元素,a是值参数,b是引用参数,c是输出参数,i是局部变量。5.1.1静态变量用static修饰符声明的字段称为静态变量。静态变量在包含了它的那个类型的静态析构函数(第10.11节)执行之前就存在了,在关联的应用程序域终止时终止。静态变量的初始值是该变量的类型的默认值(第5.2节)。出于明确赋值检查的目的,静态变量被视为初始已赋值。5.1.2实例变量未用static修饰符声明的字段称为实例变量。5.1.2.1类中的实例变量类的实例变量在创建该类的新实例时开始存在,在所有对该实例的引用都已终止,并且已执行了该实例的析构函数(若有)时终止。类实例变量的初始值是该变量的类型的默认值(第5.2节)。出于明确赋值检查的目的,类的实例变量被视为初始已赋值。5.1.2.2结构中的实例变量结构的实例变量与它所属的结构变量具有完全相同的生存期。换言之,当结构类型的变量开始存在或停止存在时,该结构的实例变量也随之存在或消失。结构的实例变量与包含它的结构变量具有相同的初始赋值状态。换言之,当结构变量本身被视为初始已赋值时,它的实例变量也被视为初始已赋值。而当结构变量被视为初始未赋值时,它的实例变量同样被视为未赋值。5.1.3数组元素数组的元素在创建数组实例时开始存在,在没有对该数组实例的引用时停止存在。每个数组元素的初始值都是其数组元素类型的默认值(第5.2节)。出于明确赋值检查的目的,数组元素被视为初始已赋值。5.1.4值参数未用ref或out修饰符声明的参数为值参数。值参数在调用该参数所属的函数成员(方法、实例构造函数、访问器或运算符)(第7.4节)时开始存在,并用调用中给定的自变量的值初始化。当返回该函数成员时值参数停止存在。出于明确赋值检查的目的,值参数被视为初始已赋值。5.1.5引用参数用ref修饰符声明的参数是引用参数。引用参数不创建新的存储位置。相反,引用参数表示的是那个在对该函数成员调用中被当作“自变量”的变量所表示的同一个存储位置。因此,引用参数的值总是与基础变量相同。下面的明确赋值规则适用于引用参数。注意第5.1.6节中描述的输出参数的不同规则。变量在可以作为引用参数在函数成员调用中传递之前,必须已明确赋值(第5.3节)。在函数成员内部,引用参数被视为初始已赋值。在结构类型的实例方法或实例访问器内部,this关键字的行为与该结构类型的引用参数完全相同(第7.5.7节)。5.1.6输出参数用out修饰符声明的参数是输出参数。输出参数不创建新的存储位置。相反,输出参数表示的是那个在对该函数成员调用中被当作“自变量”的变量所表示的同一个存储位置。因此,输出参数的值总是与基础变量相同。下面的明确赋值规则应用于输出参数。注意第5.1.5节中描述的引用参数的不同规则。变量在可以作为输出参数在函数成员调用中传递之前不一定要明确赋值。在正常完成函数成员调用之后,每个作为输出参数传递的变量都被认为在该执行路径中已赋值。在函数成员内部,输出参数被视为初始未赋值。函数成员的每个输出参数在该函数成员正常返回前都必须已明确赋值(第5.3节)。在结构类型的实例构造函数内部,this关键字的行为与结构类型的输出参数完全相同(第7.5.7节)。5.1.7局部变量局部变量是通过局部变量声明来声明的,此声明可以出现在块、for语句、switch语句或using语句中。局部变量的生存期是程序执行过程中的某一“段”,在此期间,一定会为该局部变量保留存储。此生存期从进入与它关联的块、for语句、switch语句或using语句开始,一直延续到对应的块、for语句、switch语句或using语句的执行以任何方式结束为止。(进入封闭块或调用方法会挂起(但不会结束)当前的块、for语句、switch语句或using语句的执行。)如果以递归方式进入父块、for语句、switch语句或using语句,则每次都创建局部变量的新实例,并且重新计算它的局部变量初始值设定项(如果有的话)。局部变量不自动初始化,因此没有默认值。出于明确赋值检查的目的,局部变量被视为初始未赋值。局部变量声明可包括局部变量初始值设定项,在此情况下变量被视为在它的整个范围内(局部变量初始值设定项中提供的表达式内除外)已明确赋值。在局部变量的范围内,在局部变量声明符之前的文本位置引用该局部变量是编译时错误。局部变量的实际生存期依赖于具体实现。例如,编译器可能静态地确定块中的某个局部变量只用于该块的一小部分。根据这种分析,编译器生成的代码可能会提前回收该变量的存储(相对于包含该变量的生存期)。局部引用变量所引用的存储的回收与该局部引用变量(第3.9节)的生存期无关。foreach语句和try语句的特定catch子句也声明局部变量。对于foreach语句,局部变量是一个迭代变量(第8.8.4节)。对于特定的catch子句,局部变量是一个异常变量(第8.10节)。foreach语句或特定的catch子句所声明的局部变量被视为在它的整个范围内已明确赋值。5.2默认值以下类别的变量自动初始化为它们的默认值:静态变量。类实例的实例变量。数组元素。变量的默认值取决于该变量的类型,并按下面这样确定:对于值类型的变量,默认值与该值类型的默认构造函数(第4.1.1节)所计算的值相同。对于“引用类型”的变量,默认值为null。初始化为默认值的实现方法一般是让内存管理器或垃圾回收器在分配内存以供使用之前,将内存初始化为“所有位归零”(all-bits-zero)。由于这个原因,使用所有位归零来表示空(null)引用很方便。5.3明确赋值在函数成员可执行代码中的给定位置,如果编译器可通过静态流程分析证明变量已自动初始化或已成为至少一个赋值的目标,则称该变量已明确赋值。明确赋值的规则为:初始已赋值的变量(第5.3.1节)总是被视为已明确赋值。如果所有可能通向给定位置的执行路径都至少包含以下内容之一,则初始未赋值的变量(第5.3.2节)被视为在该位置已明确赋值。将变量作为左操作数的简单赋值(第7.13.1节)。将变量作为输出参数传递的调用表达式(第7.5.5节)或对象创建表达式(第7.5.10.1节)。对于局部变量,包含变量初始值设定项的局部变量声明(第8.5节)。关于对一个结构类型变量的实例变量是否明确赋值,既可个别地也可作为整体进行跟踪。除了上述规则,下面的规则也应用于结构类型变量及其实例变量:如果一个实例变量的包含它的那个结构类型变量被视为已明确赋值,则该实例变量被视为已明确赋值。如果一个结构类型变量的每个实例变量都被视为已明确赋值,则该结构类型变量被视为已明确赋值。在下列上下文中要求实施明确赋值:变量必须在获取其值的每个位置都已明确赋值。这确保了从来不会出现未定义的值。变量在表达式中出现被视为要获取该变量的值,除非当该变量为简单赋值的左操作数,该变量作为输出参数传递,或者该变量为结构类型变量并作为成员访问的左操作数出现。变量必须在它作为引用参数传递的每个位置都已明确赋值。这确保了被调用的函数成员可以将引用参数视为初始已赋值。函数成员的所有输出参数必须在函数成员返回的每个位置都已明确赋值,返回位置包括通过return语句实现的返回,或者通过执行语句到达函数成员体结尾的返回。这确保了函数成员不在输出参数中返回未定义的值,从而使编译器能够把一个对函数成员的调用当作对某些变量的赋值,这些变量在该调用中被当作输出参数传递。结构类型实例构造函数的this变量必须在该实例构造函数返回的每个位置明确赋值。5.3.1初始已赋值变量以下类别的变量属于初始已赋值变量:静态变量。类实例的实例变量。初始已赋值结构变量的实例变量。数组元素。值参数。引用参数。在catch子句或foreach语句中声明的变量。5.3.2初始未赋值变量以下类别的变量属于初始未赋值变量:初始未赋值结构变量的实例变量。输出参数,包括结构实例构造函数的this变量。局部变量,在catch子句或foreach语句中声明的那些除外。5.3.3确定明确赋值的细则为了确定每个已使用变量都已明确赋值,编译器必须使用与本节中描述的进程等效的进程。编译器处理每个具有一个或多个初始未赋值变量的函数成员的体。对于每个初始未赋值的变量v,编译器在函数成员中的下列每个点上确定v的明确赋值状态:在每个语句的开头处在每个语句的结束点(第8.1节)在每个将控制转移到另一个语句或语句结束点的arc上在每个表达式的开头处在每个表达式的结尾处v的明确赋值状态可以是:明确赋值。这表明在能达到该点的所有可能的控制流上,v都已赋值。未明确赋值。当在bool类型表达式结尾处确定变量的状态时,未明确赋值的变量的状态可能(但不一定)属于下列子状态:在true表达式后明确赋值。此状态表明如果该布尔表达式计算为true,则v是明确赋值的,但如果布尔表达式计算为false,则不一定要赋值。在false表达式后明确赋值。此状态表明如果该布尔表达式计算为false,则v是明确赋值的,但如果布尔表达式计算为true,则不一定要赋值。下列规则控制变量v的状态在每个位置是如何确定的。5.3.3.1一般语句规则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节)相同。5.3.3.2块语句、checked和unchecked语句在指向位于某块中语句列表的第一个语句(如果语句列表为空,则指向该块的结束点)的控制转移上,v的明确赋值状态与块语句、checked或unchecked语句之前的v的明确赋值状态相同。5.3.3.3表达式语句对于由表达式expr组成的表达式语句stmt:v在expr的开头处与在stmt的开头处具有相同的明确赋值状态。如果v在expr的结尾处明确赋值,则它在stmt的结束点也明确赋值;否则,它在stmt的结束点也不明确赋值。5.3.3.4声明语句如果stmt是不带有初始值设定项的声明语句,则v在stmt的结束点与在stmt的开头处具有相同的明确赋值状态。如果stmt是带有初始值设定项的声明语句,则确定v的明确赋值状态时可把stmt当作一个语句列表,其中每个带有初始
本文标题:C第5章
链接地址:https://www.777doc.com/doc-3104035 .html