类和结构实际上都是创建对象的模板,每个对象都包含数据,并提供罗勒处理和访问数据的方法。类定义了每个类对象(实例)可以包含什么数据和功能。例如一个类表示一个顾客,就可以定义字段CustomerID,FirstName,LastName和Address,以及 包含该顾客的信息,还可以定义处理存储在这些字段中的数据的功能。
结构与类的区别是它们在内存中的存储方式(类是存储在堆(heap)上的引用类型,而结构是存储在堆栈(stack)上的值类型)、访问方式和一些特性(如结构不支持继承)。较小的数据类型使用结构可提高性能。但在语法上,结构与类非常相似,主要区别是使用关键字struct代替class来声明结构
对于类和结构,都使用关键字new来声明实例:这个关键字创建对象并对其进行初始化。
类成员
类中的数据和函数称为类的成员。除了这些成员外,类还可以包含嵌套的类型(例如其他类)。类中的所有成员都可以声明为public或private。
数据成员
数据成员包含了类的数据——字段、常量和事件。数据成员可以是静态数据(与整个类相关)或实例数据(类的每个实例都有它自己的数据副本)。通常,对于面向对象的语言,类成员总是实例成员,除非用static进行了显式声明。
字段是于类相关的变量。前面的例子已经使用了Customer类中的字段,一旦实例化Customer对象,就可以使用语法Object.FieldName来访问这些字段。
常量与类的关联方式同变量与类的关联方式一样。
事件是类的成员,在发生某些行为(如改变类的字段或属性,或者进行了某种形式的用户交互操作)时,它可以让对象通知调用程序。
函数成员
函数成员提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器(finalizer)、运算符以及索引器。
方法是与某个类相关的函数,它们可以是实例方法,也可以是静态方法。实例方法处理类的某个实例,静态方法提供了更一般的功能,不需要实例化一个类。
属性是可以在客户机上访问的函数组,其访问方式与访问类的公共字段类似。
构造函数是在实例化对象时自动调用的函数。它们必须与所属类同名,且不能有返回类型。构造函数用于初始化字段的值。
终结器(析构函数)类似于构造函数,它们的名称与类相同,但前面有一个~符号。终结器在C#中用得少,因为CLR会自动进行垃圾收集。另外不可能预测什么时候调用终结器。
运算符执行的最简单的操作就是+和-。C#还允许指定把已有的运算符应用于自己的类(运算符重载)。
索引器允许对象以数组或集合的方式进行索引。
方法
方法的声明
在C#中,每个函数成员都必须与类或结构相关。正式的C#术语实际上区分了函数和方法:“函数成员”不仅包含方法,而且月包含类或结构的一些非数据成员,例如索引器、运算符、构造函数和析构函数等,甚至还有属性。
在C#中,每个方法都单独声明为public或private,不能使用public:块把几个方法定义组合起来。所有的C#方法都在类定义中声明和定义。方法的定义包括方法的修饰符、返回值的类型、方法名、输入参数的列表和方法体。
每个参数都包括参数的类型名及在方法体中的引用名称。但如果方法体有返回值,return语句就必须与返回值一起使用,以指定出口点。
如果方法没有返回值,就把返回类型指定为void。如果不带参数,仍需要在方法名后面写上一对空的圆括号()。此时return语句是可选的,注意方法可以包含任意多个return语句。
调用方法
C#中调用方法时,必须使用圆括号。下面的例子MathTest说明了类的定义和实例化、方法的定义和调用的语法。
给方法传递参数
参数可以通过引用或值传递给方法。在变量通过引用传递给方法时,被调用的方法得到的就是这个变量,所以在方法内部对变量进行的任何改变在方法退出后仍旧发挥作用。而如果变量是通过值传递给方法的,被调用的方法得到的是变量的一个副本,在方法退出后,对变量进行的修改会丢失。
C#中所有参数都是通过值来传递的,除非特别说明。
结果是i=0,ints[0]=100。注意字符串是不同的,因为字符串是不能改变的(如果改变字符串的值,就会创建一个全新的字符串),所以字符串无法采用一般引用类型的行为方式。在方法调用中,对字符串所做的任何改变都不会影响原来的字符串。
ref参数
通过值传递变量是默认的,也可以迫使值参数通过引用方式传送给方法。需要使用ref关键字。如果把一个参数传递给方法,且这个方法的输入参数前带有ref关键字,则该方法对变量所作的任何改变都会影响原来对象的值。
在调用该方法时,还需要添加ref关键字:
out关键字
C#要求变量在被引用前必须用一个初值进行初始化。尽管在把输入变量传递给函数前,可以用没有意义的值初始化它们,因为函数将使用真实、有意义的值初始化它们,但这样做是没有必要的,有时甚至会引起混乱。C#编译器使用out关键字来初始化输入参数,当在方法的输入参数前面加上out关键字时,传递给该方法的变量可以不初始化。该变量通过引用传递,在调用该方法时,还需要使用out关键字,与在定义该方法时一样
out关键字是C#中的新增内容,该关键字的引入使C#更安全,更不容易出错。
方法的重载
C#支持方法的重载——方法的几个不同签名(名称,参数个数,参数类型)的版本,为了重载方法,只需要声明同名但参数个数或类型不同的方法即可。
因为C#不支持可选参数,所以需要使用方法重载来达到此目的。
两个方法不能仅在返回类型上有区别,不能仅根据参数是声明为ref还是out来区分。
属性
属性是一个方法或一对方法,在客户机代码看来,他们是一个字段。
在语法上,上面的代码类似于设置一个字段,但实际上是调用了属性访问器,它包含的代码重新设置了窗体的大小。
在C#中定义属性,可以使用下面的语法:
get访问器不带参数,且必须返回属性声明的类型。也不应为set访问器指定任何显式参数,但编译器假定它带一个参数,其类型也于属性相同,并表示为value。
在属性定义中省略set访问器,就可以创建只读属性。
同样,在属性定义中省略get访问器,就可以创建只写属性。
C#允许给属性的get和set访问器设置不同的访问修饰符,在get和set访问器中,必须有一个具备属性的访问级别。
JIT编译器可以生成高度优化的代码,并在适当的时候内联代码(用内联代码代替函数调用)。如果某个方法或属性的执行代码仅是调用另一个方法,或返回一个字段,则该方法或属性肯定是内联的。(在何处内联代码的决定完全由CLR作出,无法控制)。