C#的变量、表达式和基本结构.doc_第1页
C#的变量、表达式和基本结构.doc_第2页
C#的变量、表达式和基本结构.doc_第3页
C#的变量、表达式和基本结构.doc_第4页
C#的变量、表达式和基本结构.doc_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C#语言是一门简单,现代,优雅,面向对象,类型安全,平台独立的一门新型组件编程语言。其语法风格源自C/C+家族,融合了Visual Basic的高效和C/C+强大。其优雅的语法风格,创新的语言特性,因而深受世界各地程序员的好评和喜爱。C#起源于C语言家族,因此,C,C+和Java的程序员能很快熟悉它。C#获得了ECMA和ISO/IEC的国际标准认证,它们分别是ECMA-334标准和ISO/IEC 23270标准。Microsoft用于.NET框架的C#编译器就是根据这两个标准实现的。本期专题力求对C#做一个简单明了的说明。目的是向读者提供对语言的入门介绍,以便于读者能够快速上手编写程序。C#的变量、表达式和基本结构一、C#基础语法二、数据类型和变量三、类型和类型变量 四、可空类型 五、定义常量的两种方法 六、表达式 七、声明bool变量 八、使用布尔操作符 九、复合赋值操作符 十、使用if语句 十一、switch语句 十二、使用do语句 十三、C#的程序结构一、C#基础语法C#代码的外观和操作方式与C+和Java非常类似。初看起来,其语法可能比较混乱,不像书面英语和其他语言。但是,在C#编程中,使用的样式是比较清晰的,不用花太多的力气就可以编写出可读性很强的代码。与其他语言的编译器不同,无论代码中是否有空格、回车符或tab字符(这些字符统称为空白字符),C#编译器都不考虑这些字符。这样格式化代码时就有很大的自由度,但遵循某些规则将有助于使代码易于阅读。C#代码由一系列语句组成,每个语句都用一个分号来结束。因为空格被忽略,所以一行可以有多个语句,但从可读性的角度来看,通常在分号的后面加上回车符,这样就不能在一行上放置多个语句了。但一句代码放在多个行上是可以的(也比较常见)。C#是一个块结构的语言,所有的语句都是代码块的一部分。这些块用花括号来界定( 和 ),代码块可以包含任意多行语句,或者根本不包含语句。注意花括号字符不需要附带分号。所以,简单的C#代码块如下所示:;其中部分并不是真正的C#代码,而是用这个文本作为C#语句的占位符。注意在这段代码中,第2、3行代码是同一个语句的一部分,因为在第2行的末尾没有分号。在这个简单的代码块中,还使用了缩进格式,使C#代码的可读性更高。这不是我的发明,而是一个标准规则,实际上在默认情况下VS会自动缩进代码。一般情况下,每个代码块都有自己的缩进级别,即它向右缩进了多少。代码块可以互相嵌套(即块中可以包含其他块),而被嵌套的块要缩进得多一些。;前面代码的续行通常也要缩进得多一些,如上面第一个示例中的第3行代码。注释:在能通过Tools | Options访问的VS Options对话框中,显示了VS用于格式化代码的规则。在Text Editor | C# | Formatting节点的子目录下,包含了完整的格式化规则。此处的大多数设置都反映了还没有讲述的C#部分,但如果以后要修改设置,以更适合自己的个性化样式,就可以回过头来看看这些设置。在本书中,为了简洁起见,所有的代码段都使用默认设置来格式化。记住,这种样式并不是强制的。但如果不使用它,读者在阅读本书时会很快陷入迷茫之中。在C#代码中,另一个常见的语句是注释。注释并不是严格意义上的C#代码,但代码最好有注释。注释就是解释,即给代码添加描述性文本(用英语、法语、德语、外蒙古语等),编译器会忽略这些内容。在开始处理比较长的代码段时,注释可用于给正在进行的工作添加提示,例如“这行代码要求用户输入一个数字”,或“这段代码由Bob编写”。C#添加注释的方式有两种。可以在注释的开头和结尾放置标记,也可以使用一个标记,其含义是“这行代码的其余部分是注释”。在C#编译器忽略回车符的规则中,后者是一个例外,但这是一种特殊情况。要使用第一种方式标记注释,可以在注释的开头加上“/*”,在末尾加上“*/”。这些注释符号可以在单独一行上,也可以在不同的行上,注释符号之间的所有内容都是注释。注释中惟一不能输入的是“*/”,因为它会被看作注释结束标记。所以下面的语句是正确的。/* This is a comment */* And so. is this! */但下面的语句会产生错误:/* Comments often end with */ characters */注释结束符号后的内容(*/后面的字符)会被当作C#代码,因此产生错误。 另一个添加注释的方法是用“/”开始一个注释,其后可以编写任何内容,只要这些内容在一行上即可。下面的语句是正确的:/ This is a different sort of comment.但下面的语句会失败,因为第二行代码会解释为C#代码:/ So is this,but this bit isnt.这类注释可用于语句的说明,因为它们都放在一行上:; / Explanation of statement前面说过有两种方法给C#代码添加注释。但在C#中,还有第三类注释,严格地说,这是/语法的扩展。它们都是单行注释,用三个/符号来开头,而不是两个。/ A special comment在正常情况下,编译器会忽略它们,就像其他注释一样,但可以配置VS,在编译项目时,提取这些注释后面的文本,创建一个特殊格式的文本文件,该文件可用于创建文档说明书。具体内容见第28章。特别要注意的一点是,C#代码是区分大小写的。与其他语言不同,必须使用正确的大小写形式输入代码,因为简单地用大写字母代替小写字母会中断项目的编译。如果读者对C#语言没有什么了解,就很难理解这一点,看看下面这行代码,它在第2章的第一个示例中使用:Console.WriteLine(The first app in Beginning C# Programming!);C#编译器能理解这行代码,因为Console.WriteLine()命令的大小写形式是正确的。但是,下面的语句都不能工作:console.WriteLine(The first app in Beginning C# Programming!);CONSOLE.WRITELINE(The first app in Beginning C# Programming!);Console.Writeline(The first app in Beginning C# Programming!); 这里使用的大小写形式是错误的,所以C#编译器不知道我们要做什么。幸好,VS在代码的输入方面提供了许多帮助,在大多数情况下,它都知道(程序也知道)我们要做什么。在输入代码的过程中,VS会推荐用户可能要使用的命令,并尽可能纠正大小写问题。C#控制台应用程序的基本结构下面看看第2章的控制台应用程序示例(ConsoleApplication1),研究一下它的结构。其代码如下所示:using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1class Programstatic void Main(string args)/ Output text to the screen.Console.WriteLine(The first app in Beginning C# Programming!);Console.ReadKey();可以立即看出,上一节讨论的所有语法元素这里都有。其中有分号、花括号、注释和适当的缩进。目前看来,代码中最重要的部分如下所示:static void Main(string args)/ Output text to the screen.Console.WriteLine(The first app in Beginning C# Programming!);Console.ReadKey();在运行控制台应用程序时,就运行这段代码,更准确地说,是运行花括号中的代码块。如前所述,注释行不做任何事情,包含它们只为了简洁而已。其他两行代码在控制台窗口中输出了一些文本,并等待一个响应。但目前我们还不需要关心它的具体机制。这里要注意一下如何实现上一章介绍的代码突出显示功能,虽然这对于Windows应用程序来说比较重要,但它是一个非常有用的特性。要实现该功能,需要使用#region和#endregion关键字,来定义可以扩展和收缩的代码区域的开头和结尾。例如,可以修改为ConsoleApplication1生成的代码,如下所示:#region Using directivesusing System;using System.Collections.Generic;using System.Text;#endregion 这样就可以把这些代码行收缩为一行,以后要查看其细节时,可以再次扩展它。这里包含的using语句和其下的namespace语句在本章的后面解释。注释:以#开头的任意关键字实际上都是一个预处理指令,严格地说并不是C#关键字。除了这里描述的#region和#endregion关键字之外,其他关键字都相当复杂,用法也比较专业。所以,这是一个读者通读全书后才能探究的主题。现在不必考虑示例中的其他代码,因为本书前几章仅解释C#的基本语法,至于应用程序进行Console.WriteLine()调用的具体方式,则不在我们的考虑之内。以后会阐述这些代码的重要性。二、数据类型和变量在体验C#的锐利之前,关乎语言基本知识的掌握是必不可少的一环。由于C#基本语言很多源自C/C+,在这里对那些和C/C+类似的地方仅作简单介绍,我们将体验专注于那些区别于传统C/C+的关键的语言基础知识。 数据类型 C#语言的数据类型主要分为两类:值类型和引用类型。另外一种数据类型指针是为unsafe上下文编程专门设定的,其中unsafe上下文指对代码进行unsafe标示以满足利用指针对内存直接进行操作要求的C#非托管代码,这些代码将失去Microsoft.NET平台的垃圾收集等CLR性质,我们放在COM互操作 非托管编程与异常处理专题里阐述。值类型的变量本身包含他们的数据,而引用类型的变量包含的是指向包含数据的内存块的引用或者叫句柄。从下面这幅图中可以清晰地看出两者的差别: 引用类型带来的可能的问题便是当多个变量引用同样的内存块时,对任何一个引用变量的修改都会导致该对象的值的改变。null值表示引用类型没有对任何实际地址进行引用。 值类型可分为结构类型和枚举类型。结构类型包括简单类型和用户自定义结构类型。枚举类型和用户自定义结构类型我们将在第九讲 结构,枚举,数组与字符串专题里详细阐述。简单类型又可分为布尔类型和数值类型。C#语言中布尔类型严格与数值类型区分,只有true和false两种取值,不存在像C/C+里那样和其他类型之间的转换。数值类型包括整值,浮点和decimal三种类型。整值类型有sbyte,byte,short,ushort,int,uint,long,ulong,char共九种。除了char类型外,其他8种两两一组分别为有符号和无符号两种。浮点值有float和double两种。decimal主要用于金融,货币等对精度要求比较高的计算环境。下表是对这些简单类型的一个详细的描述:简单类型描 述示 例sbyte8-bit 有符号整数sbyte val = 12;short16-bit 有符号整数short val = 12;int32-bit有符号整数int val = 12;long64-bit有符号整数long val1 = 12; long val2 = 34L;byte8-bit无符号整数byte val1 = 12; byte val2 = 34U;ushort16-bit 无符号整数ushort val1 = 12; ushort val2 = 34U;uint32-bit 无符号整数uint val1 = 12; uint val2 = 34U;ulong64-bit 无符号整数ulong val1 = 12; ulong val2 = 34U; ulong val3 = 56L; ulong val4 = 78UL;float32-bit单精度浮点数float val = 1.23F;double64-bit双精度浮点数double val1 = 1.23; double val2 = 4.56D;l布尔类型bool val1 = true; bool val2 = false;char字符类型 ,Unicode 编码char val = h;decimal28个有效数字的128-bit十进制类型decimal val = 1.23M;引用类型共分四种类型:类,接口,数组,委派。类除了我们可以定义自己的类型外,又包括两个比较特殊的类型object和string。object是C#中所有类型(包括所有的值类型和引用类型)的继承的根类。string类型是一个密封类型(不能被继承),其实例表示Unicode字符串,它和数组类型我们将放在第九讲 结构,枚举,数组与字符串中详述。接口类型定义一个方法的合同,我们将在第七讲 接口 继承与多态中讲述。委派类型是一个指向静态或实例方法的签名,类似于C/C+中的函数指针,将在第八讲 委派与事件中讲述。实际上我们将从后面的专题中看到这些类型都是类的某种形式的包装。 每种数据类型都有对应的缺省值。数值类型的缺省值为0或0.0,其中char的缺省为x0000。布尔类型的缺省值为false。枚举类型的缺省值为0。结构类型的缺省值是将它所有的值类型的域设置为对应值类型的缺省值,将其引用类型的域设置为null。所有引用类型的缺省值为null。 不同类型的数据之间可以转换,C#的类型转换有隐含转换,明晰转换,标准转换,自定义转换共四种方式。隐含转换与明晰转换和C+里一样,数据从小类型到大类型的转换时为隐含转换,从大类型到小类型的转换为明晰转换,明晰转换需要如(Type)data一般的括号转换操作符。标准转换和自定义转换是针对系统内建转换和用户定义的转换而言的,两者都是对类或结构这样的自定义类型而言的。变量与常量 变量表示存储位置,变量必须有确定的数据类型。C#的类型安全的含义之一就是确保变量的存储位置容纳着合适的类型。可以将C#中的变量分为静态变量,实例变量,传值参数,引用参数,输出参数,数组参数和本地变量共七种。本地变量则是在方法体内的临时变量。 静态变量和实例变量主要是针对类或结构内的数据成员(又叫域)而言的。静态变量在它寄存的类或结构类型被装载后得到存储空间,如果没有对它进行初始化赋值,静态变量的初始值将是它的类型所持有的缺省值。实例变量在它的类实例被创建后获得存储空间,如果没有经过初始化赋值,它的初始值与静态变量的定义相同。两者更详细的说明我们放在第六讲 域 方法 属性与索引器专题里。 传值参数,引用参数,输出参数,数组参数主要针对方法的参数类型而言的。简单的讲传值参数是对变量的值的一种传递,方法内对变量的改变在方法体外不起作用。对于传值参数本身是引用型的变量稍有不同,方法内对该引用(句柄)变量指向的数据成员即实际内存块的改变将在方法体外仍然保留改变,但对于引用(句柄)本身的改变不起作用。引用参数是对变量的句柄的一种传递,方法内对该变量的任何改变都将在方法体外保留。输出参数是C#专门为有多个返回值的方法而量身定做的,它类似于引用变量,但可以在进入方法体之前不进行初始化,而其他的参数在进入方法体内C#都要求明确的初始化。数组参数是为传递大量的数组元素而专门设计的,它从本质上讲是一种引用型变量的传值参数。它们更详细的阐述我们也放在第六讲 域 方法 属性与索引器专题里。 本地变量严格的讲是在C#的块语句,for语句,switch语句,using语句内声明的变量,它的生命周期严格地被限制在这些语句块内部。 常量在编译时便确定它的值,在整个程序中也不许修改。常量声明的同时必须赋值。由于它的编译时确定值的特性,引用类型可能的值只能为string和null(除string外,引用类型的构建器必须在运行时才能确定引用类型的值)。 操作符与表达式 C#保留了C+所有的操作符,其中指针操作符(*和-)与引用操作符(&)需要有unsafe的上下文。C#摈弃了范围辨析操作符(:),一律改为单点操作符(.)。我们不再阐述那些保留的C+的操作符,这里主要介绍C#引入的具有特殊意义的几个操作符:as,is,new, typeof,sizeof,stackalloc。 as操作符用于执行兼容类型之间的转换,当转换失败时,as 操作符结果为null。is 操作符用于检查对象的运行时类型是否与给定类型兼容,当表达式非null且可以转化为指定类型时,is操作符结果为true,否则为false。as和is操作符是基于同样的类型鉴别和转换而设计的,两者有相似的应用场合。实际上expression as type相当于expression is type ? (type)expression : (type)null。 作为操作符的new用于在堆上创建对象和调用构造函数,值得注意的是值类型对象(例如结构)是在堆栈上创建的,而引用类型对象(例如类)是在堆上创建的。new也用于修饰符,用于隐藏基类成员的继承成员。为隐藏继承的成员,使用相同名称在派生类中声明该成员并用 new 修饰符修改它。typeof 运算符用于获得某一类型的 System.Type 对象,我们将在第十讲 特征与映射里结合Microsoft.NET的类型系统对它作详细的阐述。sizeof 运算符用于获得值类型(不适用于引用类型)的大小(以字节为单位)。stackalloc用于在堆栈上分配内存块, 仅在局部变量的初始值设定项中有效,类似于C/C+语言的_alloca。sizeof和statckalloc都由于涉及内存的直接操作而需要unsafe上下文。 C#里的某些操作符可以像C+里那样被重载。操作符重载使得自定义类型(类或结构)可以用简单的操作符来方便的表达某些常用的操作。 为完成一个计算结果的一系列操作符和操作数的组合称为表达式。和C+一样,C#的表达式可以分为赋值表达式和布尔表达式两种,C#没有引入新的表达式形式,我们对此不再赘述。 命名空间与语句 C#采用命名空间(namespace)来组织程序。命名空间可以嵌套。using指示符可以用来简化命名空间类型的引用。using指示符有两种用法。using System;语句可以使我们用简短的类型名Console来代替类型System.Console。using Output = System.Console;语句可以使我们用别名Output来代替类型System.Console。命名空间的引入大大简化了C#程序的组织方式。 C#语句可以分为标号语句,声明语句,块语句,空语句,表达式语句,选择语句,反复语句,跳转语句,try语句,checked/unchecked语句,lock语句,using语句。 标号语句主要为goto跳转设计,C#不允许跨方法的跳转,但允许小规模的方法内的跳转。声明语句可以同时进行初始化赋值,对象的实例化声明需要new关键字。块语句采用和定义语句块,主要是界定局部变量的作用范围。空语句在C#中用分号;表示,没有执行语义。表达式语句通过表达式构成语句。 选择语句有if语句和switch语句两种,与C+别无二致。反复语句除了while,do,for三种循环结构外引入了foreach语句用于遍历集合中所有的元素,但这需要特定的接口支持,我们在后面的章节里对之作详细阐述。 跳转语句有break,continue,goto,return,throw五种语句,前四种与C+里的语义相同,throw语句与后面的try语句我们将在第十一讲 COM互操作 非托管编程与异常处理阐述。 checked/unchecked语句主要用于数值运算中溢出检查的上下文。lock语句主要用于线程信号量的锁控制。using语句主要用于片断资源管理。这些我们在后续章节里都会有具体的涉及。三、类型和类型变量 C#中有两种类型:值类型(value type)和引用类型(reference type)。值类型变量直接包括它们的数据,而引用类型变量存储的是它们的数据引用,后者被认为是对象。对于引用类型,有可能两个变量引用相同的对象,因此对其中一个变量的操作可能影响另一个对象引用的对象。对于值类型,每个变量都有自己的数据拷贝,因此对一个变量的操作不可能影响其他变量(ref和out参数变量例外)。C#的值类型进一步划分为简单类型(simple type)、枚举类型(enum type)和结构类型(struct type);C#的引用类型进一步划分为类类型(class type)、接口类型(interface type)、数组类型(array type)和委托类型(delegate type)。表1.1为整个C#类型系统的概述。表1.1 C#类型系统的概述类 别描 述值类型简单类型有符号整型:sbyte,short,int,long无符号整型:byte,ushort,uint,ulongUnicode字符:charIEEE浮点型:float,double高精度小数:decimal布尔型:bool枚举类型用户自定义类型enum E结构类型用户自定义类型struct S引用类型类类型所有其他类型的最终基类:objectUnicode字符串:string用户自定义类型class C接口类型用户自定义类型interface I数组类型单维与多维数组,例如,int与int,委托类型用户自定义类型delegate T D()8个整型类型分别支持8位、16位、32位和64位整数的有符号或者无符号格式。两个浮点类型,float和double,分别用32位单精度和64位双精度的IEEE754格式表示。decimal是128位的数据类型,适合财金和货币方面的计算。C#的bool类型用于表示布尔值true或者false。在C#中,字符和字符串的处理使用Unicode编码。char类型表示16位的Unicode编码单元,string类型表示16位的Unicode编码单元的序列。表1.2总结了C#的数值类型。表1.2 C#的数值类型类 别位 数类 型范围精度有符号整型8sbyte-12812716short-32 76832 76732int-2 147 483 6482 147 483 64764long-9 223 372 036 854 775 8089 223 372 036 854 775 807无符号整型8byte025516ushort06553532uint04 294 967 29564ulong018 446 744 073 709 551 615浮点型32float1.510-453.41038,7位精度64double5.010-3241.710308,15位精度Decimal128decimal1.010-287.91028,28位精度C#程序使用类型声明创建新类型。类型声明指定新类型的名字和成员。有5种C#类型可由用户自定义:类类型、结构类型、接口类型、枚举类型和委托类型。l类类型定义了一个数据结构,它包括数据成员(字段)和函数成员(方法、属性及其他)。类类型支持继承和多态,即派生类能够扩展和特殊化基类的机制。l结构类型与类类型相似,表示带有数据成员和函数成员的结构。然而,与类类型不同的是,结构是值类型,不需要堆分配。结构不支持用户指定的继承,所有的结构类型隐式地继承类型object。l接口类型定义了一个约定,作为一组函数成员命名的集合。实现接口的类或结构必须提供接口函数成员的实现。接口可能从多个基接口继承而来,类或结构也可能实现多个接口。l枚举类型是带有命名常量的独特类型。每个枚举类型有一个底层的类型,它必须是8个整型类型之一。枚举类型的值集与底层类型的值集相同。l委托类型通过特定的参数列表和返回类型表示对方法的引用。委托将方法处理为实体,实体能够赋值给变量,并且当做参数传递。委托类似于某些程序语言中的方法指针,不同之处在于,委托是面向对象的,并且是类型安全的。C#支持任何类型的一维和多维数组。不同于其他类型,数组类型在它们被使用前不必声明。事实上,数组类型的构造是由某个类型名加上方括号。例如,int是int的一维数组,int,是int的二维数组,而int是int的一维数组的一维数组。 C#的类型系统是统一的,这样任何类型的值都能够被处理成对象。C#中每一个类型直接或者间接从object类继承而来,并且object是所有类型最终的基类。值类型的值可以通过执行装箱(boxing)和取消装箱(unboxing)的操作处理为对象。在下面的示例中,int被转换为object,然后又转回到int。using System;class Test static void Main() int i=123; object o=i; /装箱 int j=(int)o; /取消装箱 当值类型的值被强制类型转换为object时,就会分配持有该值的对象实例(也称为“箱子”),并且值也被拷贝到那个箱子里。相反地,当object引用被强制类型转换为值类型时,要检查这个引用类型是否是当前值类型的箱子,如果是的话,箱子中的值就会被拷贝出来。C#统一的类型系统意味着值类型能够“按需”转换为对象。由于这种统一性,使用object类型的通用类库,例如.NET框架中的集合类,能够通过引用类型和值类型使用。C#中存在几种变量,包括字段、数组元素、局部变量和参数。变量表示了存储的位置,并且每一个变量都有一个类型,以决定什么样的值能够存入变量中,如表1.3所示。表1.3 C#的变量变量类型可能的内容值类型值类型的值objectnull引用、任何引用类型的对象引用或任何值类型装箱值的引用类类型null引用、该类类型实例的引用或由该类类型派生类的实例的引用接口类型null引用、实现该接口的类类型实例的引用或实现该接口的值类型装箱值的引用数组类型null引用、该数组类型实例的引用或兼容数组类型实例的引用委托类型null引用或该委托类型实例的引用四、可空类型 随着C#语言最新标准的出炉,现在它也提供了对可空类型的支持。这个小变化将会在处理那些包括可选项的数据库记录时非常有用。当然在其他地方,它也是非常有用的。简单说来,可空数据类型就是包含了所定义的数据类型或者值的空(null)的类型。C#的ECMA-334标准提供了对所有C#值类型的可空版本的描述。 定义可空类型定义可空类型和非可空类型基本类似,不同的是采用了?来表示。如定义一个整型,你可以使用简单的语句:int myInt = 1;为了使得myInt能够存储一个空值,你可以这样声明它:int? myNullableInt = 1;你可以看到,这两个变量看上去好像是一样的。但是,可空类型的版本是非常不同的。可空的版本事实上是一个结构,它将值类型和一个标记该值是否为空的标志位结合在一起。一个可空类型有两个公共可读的属性,HasValue和value。如果存储了一个值那么HasValue这个布尔型变量就为true。否则,如果变量是空值就是false。如果HasValue是true,你可以获取这个变量的值。如下有两个对可空变量的有效赋值:double? myDouble = 3.1415926;double? myOtherDouble = null;你可以看到,myDouble被赋值了,但是也可以被赋为空。在第二个语句里,myOtherDouble被初始化一个空值,这在一个非可空类型里不能这样做的。使用可空类型可空类型可以像普通值类型一样的使用。事实上,可以使用内建的隐式转换来转换相同类型的可空变量和非可空变量。这意味着你可以在一个标准整型和可空整型之间相互转换:int? nFirst = null;int Second = 2;nFirst = Second; / 有效nFirst = 123; / 有效Second = nFirst; / 同样有效nFirst = null; / 有效Second = nFirst; / 例外,后者是非空类型在以上的语句里,你可以看到如果可空变量不包含空值的话是可以和非可空变量交换值的。如果它是一个空值,那么就会抛出例外。为了防止例外,你可以使用可空变量的HasValue属性:if (nFirst.HasValue) Second = nFirst;你可以看到,如果nFirst有值赋值就会发生,否则程序会跳过此句语句。使用可空类型的操作符虽然可以使用相同值类型的可空和非可空变量的转换,也必须对操作符进行一些改变使得它们可以处理可空和非可空值。这些操作符被称为提升的操作符。考虑如下代码:int ValA = 10;int? ValB = 3;int? ValC = ValA * ValB;在ValC里存储了什么?ValC中存储了30。标准操作符被扩展使得它们能够处理可空类型。考虑到如下的变化:int ValA = 10;int? ValB = null;int? ValC = ValA * ValB;ValC这次值为多少?ValC为空。无论哪个操作数为空,提升的操作符的结果为空。即使进行加法或减法,结果也为空。如果ValC不为可空类型呢?如下的代码会有什么样的结果?int ValA = 10;int? ValB = null;int ValC = ValA * ValB; / ValC 不为可空类型代码将会抛出一个异常。ValA*ValB结果为空,但是不能赋值为非可空类型,这将会导致程序异常的抛出。比较比较将会和数学计算操作类似的方式处理。比较的操作数将同时被提升为可空的。这样就可以比较了,如果某个操作数为空,那么比较结果为false。如果对比是否相等,两个同为空的变量将被认为是相等的。一个空变量和其他任意值的变量相比的结果是不相等。下面是一些比较的例子:int abc = 123;int xyz = 890;int? def = null;int? uvw = 123;Comparison Resultabc = xyz / falseabc = def / falsedef = null / trueabc = uvw / trueuvw = null / falseuvw != null / true在所有的比较中,结果都是布尔型值true或者false。在做大小比较的时候,如果操作数的任意一个或者都是空值,那么结果返回的是false。如下展示了一些例子: Comparison Resultabc uvw / false, they are equalabc def / false, def is nulluvw null / false, because right side is nulluvw null / false, because right side is null可空性的移去C#在新版本中加入了一个新的操作符,它被称为空接合操作符,使用如下的格式:returnValue = first second;这样,如果first不为空,那么它的值将返回作为returnValue的值。如果first为空,那么second的值将被返回。注意:returnValue可以为可空变量或者非可空变量。 如果你希望可空变量的值到一个非可空的版本,你可以这样做:int? ValA= 123;int? ValB = null;int NewVarA = ValA ? -1;int NewVarB = ValB ? -1;NewVarA的值将会为123因为ValA不是空值。NewVarb的值是-1因为ValB是空值。你看一看到,这里你将可以将变量从一个空值转化成一个缺省值。这里缺省值是-1。结束语总得来说,最新的C#允许一个可空类型的存在。语言内部建立了对可空类型的处理机制。可空类型使得数据库记录和其他可选信息更加的容易处理。可空类型是C# ECMA-334版本的一个特性。你需要一个支持这个版本的C#的编译器。Visual Studio 2005支持这个版本。五、定义常量的两种方法 在C#中定义常量的方式有两种,一种叫做静态常量(Compile-time constant),另一种叫做动态常量(Runtime constant)。前者用“const”来定义,后者用“readonly”来定义。 对于静态常量(Compile-time constant),它的书写方式如下:public const int MAX_VALUE = 10;为什么称它为静态常量呢,因为如上声明可以按照如下理解(注意:如下书写是错误的,会出编译错误,这里只是为了方便说明)。public static const int MAX_VALUE = 10;用const定义的常量,对于所有类对象而言都是一样的,因此需要像访问静态成员那样去访问const定义的常量,而用对象的成员方式去访问会出变异错误。此外,对于静态常量的访问在编译的时候,是用常量的值去替换常量,例如:int nValue = MAX_VALUE;这句在编译之后,和如下这句所产生的中间语言代码是一样的。int nValue = 10;不过,在用const来定义常量的时候,在类型上有很多限制。首先,此类型必须属于值类型,同时此类型的初始化不能通过new来完成,因此一些用struct定义的值类型常量也不能用const来定义。相对于const而言,用readonly来定义常量要灵活的多,它的书写方式如下:public readonly int MAX_VALUE = 10;为什么称为动态变量,因为系统要为readonly所定义的常量分配空间,即和类的其他成员一样拥有独立的空间。此外,readonly所定义的常量除了在定义的时候可以设定常量值外,还可以在类的构造函数中进行设定。由于readonly所定义的常量相当于类的成员,因此使用const来定义常量所受到的类型限制,在使用readonly去定义的时候全部消失,即可以用readonly去定义任何类型的常量。综合上面所述,至于对比两者之间的区别具体如下。静态常量(Compile-time constant)动态常量(Runtime constant)定义声明的同时要设置常量值。声明的时候可以不需要进行设置常量值,可以在类的构造函数中进行设置。类型限制首先类型必须属于值类型范围,且其值不能通过new来进行设置。没有限制,可以用它定义任何类型的常量。对于类对象而言对于所有类的对象而言,常量的值是一样的。对于类的不同对象而言,常量的值可以是不一样的。内存消耗无。要分配内存,保存常量实体。综述性能要略高,无内存开销,但是限制颇多,不灵活。灵活,方便,但是性能略低,且有内存开销。对于在定义常量的时候,到底是用const来定义还是readonly来定义,我以前为了追求性能,因此尽量用const来定义。但是在此书中,提到了一个关于使用const会产生潜在的bug。就是在程序中使用DLL类库某个类的静态常量时,如果在类库中修改静态常量的值,其它接口没有发生变化,一般来说,程序调用端是不需要重新编译,直接执行就可以调用新的类库。不过就是在此情况下,会产生潜在的bug。这是由于静态常量在编译的时候,是用它的值去替换常量,因此在调用端的程序也是这样进行替换的。例如:在类库中定义了一个静态常量,如下:public const int MAX_VALUE = 10;那么对于程序中调用此静态常量这段代码,在编译后产生的中间语言代码中,是用10来进行替换,即使用静态常量的地方,改为10了。那么当类库的静态变量发生变化后,例如:public const int MAX_VALUE = 15;那么对于调用端程序是可以在没有重新编译的情况下进行运行,不过此时程序的中间语言代码对应于静态变量的值是10,而不是新类库中的15。因此这样产生的不一致,程序会引发潜在的bug。解决此类问题的方法,就是调用端程序在更新类库之后重新编译一下,即生成新的中间语言代码。对于如上在const定义常量时所存在的潜在bug,在用readonly定义常量时是不会发生的。因为readonly定义的常量类似于类的成员,因此在访问的时候需要根据具体常量地址来访问,从而避免此类bug。鉴于此,本书建议用readonly来替换const去定义常量。六、表达式 表达式(expression)由操作数(operand)和运算符(operator)构成。表达式的运算符标明在操作数上运用了哪种操作。运算符的例子包括+、-、*、/和new。操作数的例子包括字面值、字段、局部变量和表达式。当表达式包括多个运算符时,运算符的优先级(precedence)控制各个运算符执行的顺序。例如,表达式x+y*z将以x+(y*z)的形式计算,原因就是运算符“*”的优先级高于运算符“+”。大多数运算符能够被重载(overload)。运算符的重载允许用户自定义运算符实现,用于为用户自定义的类或者结构类型指定操作方式。表1.4总结了C#的运算符,运算符的分类排列是按其优先级从高到低的次序。同一分类的运算符具有相同的优先级。表1.4 C#的运算符分 类表 达 式描 述基本x.m成员访问x()方法和委托调用x数组和索引器访问x+后增量(post-increment)x-后减量(post-decrement)new T()对象和委托创建new T数组创建typeof(T)获得T类型的System.Type对象checked(x)在检查的上下文计算表达式unchecked(x)在未检查的上下文计算表达式一元+x表达式的值相同-x求相反数!x逻辑求反x按位求反+x前增量(pre-increment)-x前减量(pre-decrement)(T)x显式地将x的类型转换为类型T乘除法x*y乘x/y除x%y求余加减x+y加,字符串合并,委托组合x-y减,委托移除移位xy右移关系和类型检测xy大于x=y大于或者等于x is T如果x属于T类型,返回true;否则,返回falsex as T返回转换为类型T的x;如果x不是T,就返回null译注2相等x=y等于x!=y不等于逻辑与x&y整型按位与,布尔型逻辑与逻辑异或xy整型按位异或,布尔型逻辑异或逻辑或x|y整型按位或,布尔型逻辑或条件与x&y如果x为true,则计算y条件或x|y如果x为false,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论