3章 C# 2008中面向过程的高级元素 • 在第2章里,介绍了C# 2008语言中简单的变量类型,但是只有这些数据类型还是不够的。本章将介绍两种常用而又简单的类型,即结构体和枚举。 • 同时在本章中,还将介绍函数的的相关内容。函数是C# 2008程序中的重要组成部分,用于实现部分特定的功能。函数所涉及到的内容非常多,函数的使用也非常灵活,本章将要介绍函数中最基本和最重要的知识。 3.1 结构体 • 当需要将不同类型的简单数据组合成一个有机的整体时,选择结构体无疑是一个聪明的选择。例如下面的情况: • 表示平面上的一个点。 • 表示由两个数(分子和分母)组成的分数。 • 通讯录里包含姓名、电话和地址的记录。 • 结构通常存放的类型必须是在内存中有固定大小,即内建的基本类型int32、int64、double等。 3.1.1 结构体的概念 • 【本节示例参考:\示例代码\Chap03\Struct】 • 生成结构的过程,就是把一系列相关的变量组织成为一个单一实体的过程。每一个变量称为结构的成员。定义结构的方式如下: • struct_modifier struct struct_name • { • Data members //数据成员 • Methods //方法 • Nested type definitions //嵌套类型 • } 3.1.2 结构体的特点 • 在结构体里包含的成员类型没有限制,可以相同也可以不同。例如可以在上一小节的PhoneBook例子中,添加一个int类型的成员。 • public struct PhoneBook • { • Public string name; • Public string age; • Public string phone; • Public string address; • Public int address_number; • } 3.2 枚举 • 本节将介绍一种常用而又简单的变量类型——枚举。枚举是一个指定的常数,其基础类型可以是除char以外的任何整型。 3.2.1 枚举的概念 • 【本节示例参考:\示例代码\Chap03\Enum】 • 枚举实际上是为一组在逻辑上密不可分的整数值提供便于记忆的符号,即创建一系列的别名,有了别名可以让程序更容易理解。定义的格式如下: • enum_modifiers enum enum_name • { • name1 = value1, • name2 = value2, • …… • nameN = valueN • } 3.2.2 应用枚举 • 【本节示例参考:\示例代码\Chap03\Enum2】 • 访问枚举元素的方式很简单,只需要输入枚举的名称、一个句号和字段的名称就可以了。例如: • MyDirection md = MyDirection.East; 3.3 函数 • 迄今为止,所看到的代码都是以代码块的形式出现的,如果对数据执行某种操作,只需要把代码块放在合适的地方就可以了。这种代码结构的作用是有限的,例如某些任务常常需要重复执行多次,即同一代码块需要执行好几次,这个问题的解决方法就要使用函数。在C# 2008中,函数是一种方法,可提供在应用程序中任何一处执行的代码块。函数的内容很多、很灵活,本节只介绍函数一些常用功能。 3.3.1 函数的基本概念 • 函数有许多优点,C# 2008语言提供了对函数的全方位支持,下面用一个程序介绍了在代码中如何定义和使用函数,其功能是在窗口上打印出一个由*组成的矩形,如代码和代码所示。 • 【本示例参考:\示例代码\Chap03\NoUseFunction】 【本示例参考:\示例代码\Chap03\UseFunction】 • 代码 “*”型矩形:UseFunction 3.3.2 函数的返回值 • 【本节示例参考:\示例代码\Chap03\Function2】 • 在上一小节中,函数虽然都没有返回任何值,但是实际上是可以返回各种形式的返回值。常用的格式是: • static return-type function-name() • { • return return-data; • } 3.3.3 函数的参数 • 在上一小节中,介绍了如何从函数中返回数值,这一节中将介绍如何给函数传入数值,即带有参数的函数。通过合理地使用参数,可以起到增强函数的作用,带有参数函数的基本格式: • static return-type function-name(data-type data-name,……) • 在上述基本格式中,data-type是参数的类型,data-name是要传入的参数的名称。当参数个数不只有一个时,参数之间用逗号分开,读者可以根据自己的需要来定义参数的个数。代码演示了函数参数的用法。 3.4 函数参数类型 • C# 2008中函数的参数有四种类型: • 值参数,不包含任何修饰符。 • 输入引用参数,以ref修饰符声明。 • 输出引用参数,以out修饰符声明。 • 数组型参数,以params修饰符声明。 3.4.1 值参数 • 当利用值参数向方法传递参数时,编译程序会把参数值复制一份传递给函数。所以果函数的作用不会作用在实际参数上。代码中演示了值参数类型函数的使用。 • 【本示例参考:\示例代码\Chap03\Function5】 • 运行结果: • x的值为3,y的值为2 • 在上述程序中,当程序执行到Swap(x,y)时编译器会调用Swap(int x,int y)函数,但被调用函数的执行并不影响主函数中x、y的值,因为传递给其的值是一份复制。如果真的要交换主函数中x、y的值,就需要用到下面的引用参数。 3.4.2 输入引用参数 • 引用参数并不开辟新的内存区域,当利用引用型参数向方法传递形参时,编译程序将把实际值在内存中的地址传递给方法。ref关键字后应跟与形参的类型相同的类型声明。代码演示了输入引用类型的函数。 • 【本示例参考:\示例代码\Chap03\Function6】 • 运行结果: • x的值为2,y的值为3 • 从上面代码的运行结果可以看出,在代码中,调用Swap()交换函数时,却没有实现交换;但在代码中,调用Swap()交换函数时,却交换了两个数的值。结果如此的差异,就源于关键字ref。 3.4.3 输出型参数 • 与输入型参数引用重载不同的是,虽然也不开辟新的内存区域,但在调用方法前无需对变量进行初始化。 • out关键字后应跟随与形参的类型相同的类型声明。在方法调用前不会被初始化,可是在方法返回后,传递的变量必须被初始化。代码演示了输出型的函数。 • 【本示例参考:\示例代码\Chap03\Function7】 • 运行结果: • x的值为2,y的值为3 3.4.4 数组型参数 • 在C# 2008中,当参数个数不定时,可以使用params关键字来定义。参数数组可以简化代码,因为不必从调用代码中传递数组,而可以传递可在函数中使用的一个数组中相同类型的几个参数。代码演示了数组型的函数的使用。 • 【本示例参考:\示例代码\Chap03\Function8】 • 运行结果: • 总值为23 • 在上述代码中,函数MyAdd(params int[ ] x)是用关键字params定义的,可以接受任意个int参数。在被调用的函数中,通过数组x中的值迭代,把传入的值加在一起,返回其结果。 3.5 函数的重载 • 在代码中,如果x、y的值为double型,那么当把x、y值传给被调用的函数MyFunction(int a,int b)时,就会产生错误。因为实参x、y的类型与虚参a、b的类型不一致。这种单一功能的函数使用时限制非常大,所以在C# 2008中提供了函数重载这一功能,同时函数重载也体现了面向对象的多态性。 3.5.1 参数类型重载的函数 • 迄今为止在调用函数的时候,必须使实参和形参的类型一致、个数和顺序要相同。为了解决上面的问题,更为了使的程序的开发更有效率并且使函数的调用更简单,C# 2008中出现了函数重载,即名称相同,但参数或返回值不同的函数。为了深刻的理解参数类型重载,代码演示了参数类型重载的函数。 • 【本示例参考:\示例代码\Chap03\Function9】 • 运行结果: • x与y的和为:5 • x与y的和为:5.8 • x与y的和为:hello C# 2008 3.5.2 输入型参数引用重载 • 代码3-10说明了ref的作用,在这里用代码演示了输入型参数引用重载的函数。【本示例参考:\示例代码\Chap03\Function10】 • 运行结果: • x与y的和为:5 • x与y的和为:5 • 从上述代码中,可以看出,编译器根据是否带有ref关键字自动选用了相应的函数。 3.5.3 输出型参数引用重载 • 与输入型参数引用重载不同的是,虽然输出型参数也不开辟新的内存区域,但在调用方法前无需对变量进行初始化。 • out关键字后应跟随与形参的类型相同的类型声明。在方法调用前不会被初始化,可是在方法返回后,传递的变量必须被初始化,代码演示了输入型参数引用重载的函数。 • 【本示例参考:\示例代码\Chap03\Function11】 • 运行结果: • x的值为3,y的值为2 3.5.4 参数个数重载的函数 • 函数重载还可以是根据参数个数来区分,代码演示了参数个数重载的函数。 • 【本示例参考:\示例代码\Chap03\Function12】 • 运行结果: • x与y的和为:5 • x与y的和为:8 • 在上述代码中,编译器根据参数的个数来自动选择调用了相应的MyFunction()函数。 3.6 变量的作用域 • 变量的作用域,决定了变量的可见性和生命周期。下面将一一介绍各种类型的变量的作用域。 3.6.1 代码块的作用域 • 代码块由若干语句组成,必须用大括号括起来。一个代码块同时也是一个复合语句,多个复合语句可以嵌套在另外的一对大括号中,形成更复杂的复合语句。代码块决定了变量的作用域,代码演示了代码块作用域的变量。 • 【本示例参考:\示例代码\Chap03\Scope1】 • 上面的代码通不过编译,因为“q=x”语句超过了q的作用域。在定义变量的语句所属的那层大括号之间,就是这个变量的有效作用范围。 3.6.2 局部变量 • 在一个函数里面的代码块中定义的变量称为局部变量,其在函数被执行时创建,在函数结束时被销毁,代码演示了局部范围的变量。 • 【本示例参考:\示例代码\Chap03\Scope3】 • 当执行该函数时候,编译器会提示MyFunction()中x、y两个变量没有定义。在上述程序中虽然主函数中定义了两个变量x、y,但它们跟MyFunction()中“return x+y;”的x、y是不一样的。 3.7 特殊函数 • 【本节示例参考:\示例代码\Chap03\】 • 在每个程序代码中,在第一章中,曾给出一个实例,如代码所示。 • 【本示例参考:\示例代码\Chap03\Hello】 • 在整个程序中只存在一个名为Main的函数,是程序的入口点,程序将在此处创建对象和调用方法。一个程序只能有一个入口点,即一个函数只能有一个Main()函数。 • Main()函数是有参数,基本格式有下面四种: • static void Main() • static void Main(string[ ] args) • static int Main() • static int Main(string[ ] args) 3.8 小结 • 本章讲述了C# 2008中两种复杂的类型结构和枚举,同时还介绍了函数。结构跟枚举的学习可以参考上一章的内容来学习。 • 函数内容非常多,非常复杂,使用非常灵活。本章从函数的参数、返回值开始,然后介绍了函数重载,变量的作用域。最后,我们介绍了Main()这一特殊函数。