第4章 面向对象程序员
• 到目前止,主要学习了C# 2008的基本语法:如何声明变量,如何控制流程等。C# 2008是一个彻底面向对象的语言,要写出语法正确、设计合理的好代码,必须掌握面向对象的特性。从本章开始,咱们将迈进面向对象的大门,即面向对象的思想将取代面向过程的思想。
4.1 类
• C# 2008秉承了C++面向对象的全部关键概念:封装、继承和多态性。其类型模型是创建在.NET虚拟对象系统之上的。类是面向对象的程序设计的基本构成模块。从定义上讲,类是一种数据结构,这种数据结构能够包含数据成员、函数成员等类型。其中数据成员类型有常量和事件;函数成员类型有方法、属性和索引器等。
4.1.1 面向对象的概念
• 随着计算机的应用愈来愈普遍,社会对软件开发提出了更高的要求。然而软件技术的进步却远远落后于硬件技术的进步,人们经常没法控制软件开发的周期和成本,软件质量老是没法让人满意,即所谓的软件危机。
• 为了摆脱软件危机,必须按照工程化的原则和方法来组织软件开发工做。在涉及大量计算的问题上,面向过程的设计方法暴露了愈来愈多的不足。例如:
• 功能与数据分离,不符合对现实世界的认识。
• 基于模块的设计方式,致使软件修改困难。
• 自顶向下的设计方法,限制了软件的可重用性,下降了开发效率,也致使最后开发出来的系统难以维护。
4.1.2 定义一个类
• 类是C#中的两种基本封装结构之一(另外一个是结构)。每一个可执行语句必须放在类或结构中。简单的说,类是一种抽象的数据类型,可是抽象的程度可能不一样。而对象就是一个类的实例。即类是对象的蓝图。
• 1.定义类
• 建立一个用户定义的类很是简单。的声明格式以下:
• attributes class-modifiers class identifier class-base
• {
• data members //数据成员
• function //函数
• nested types //嵌套类型
• }
• 2.类的修饰符
• 类的修饰符能够是如下几种之一或者是它们的组合在类的声明中同一修饰符不容许出现屡次。类的修饰符有abstract、internal、new、private、protected、public或sealed关键字。其中internal、private、protected和public关键字定义了对类的访问。在类中声明的类只能有public和internal访问属性。嵌套类能够具备以上4种访问类型中的一种,还能够有protected internal访问类型,它等效于“受保护的或内部的”访问。其余关键字涉及成员隐藏和类是否可以被实例化或继承。
4.1.3 类成员的修饰符
• 类的成员能够分为两大类:类自己所声明的,以及从基类中继承而来的。在编写程序时,能够对类的成员使用不一样的访问修饰符从而定义它们的访问级别。类的成员有如下类型,如表所示。
4.1.4 类的构造函数
• 【本节示例参考:\示例代码\Chap04\UseRectangular】
• 构造函数用于执行类的实例的初始化。每一个类都有构造函数,即便咱们没有声明它,编译器也会自动地为咱们提供一个默认的构造函数。在访问一个类的时候,系统将最早执行构造函数中的语句。实际上,任何构造函数的执行都隐式地调用了系统提供默认的构造函数base()。
• 1.构造函数的概念
• 2.构造函数的访问类型
4.1.5 类的析构函数
• 【本节示例参考:\示例代码\Chap04\Destructor】
• 在类的实例超出范围时,要确保其所占的存储能被回收,因而就出现了析构函数。在C++中,其是用来释放内存并执行清除操做的。C#使用一个垃圾收集器来自动完成大部分的相似工做。析构函数定义方式以下:
• class Myclass
• {
• ~ Myclass() //类Myclass的析构函数
• {
• Perform cleanup operations
• }
• }
4.1.6 结构与类的区别
• 在上一章中已经介绍过结构。下面让咱们看看结构和类的区别。
• 1.实例在内存中的位置以及内存回收
• 2.结构不支持继承
• 3.结构的默认值
• 4.关键字this的含义
4.1.7 类的继承
• 【本节示例参考:\示例代码\Chap04\TestInheritance】
• 若是全部的类都处在同一级别上,这中没有相互关系的平坦结构就会限制了系统面向对象的特性。继承的引入,就是在类之间创建一种相交关系,使新定义的派生类的实例能够继承已有的基类的特征和能力并且能够加入新的特性或者是修改已有的特性,创建起类的层次。在C#中,定义类的继承的语法格式以下所示:
• class subclass :baseclass
• {
• //类的成员和方法
• }
4.2 接口
• 接口是面向对象编程技术的一个重要内容。在代码中,接口负责功能的定义;在项目中,接口负责类和结构的规范。同时其能声明属性、索引器、事件和方法的编程构造,不为这些成员提供实现,只提供定义。虽然,一个类只能继承一个基类,但它却能够实现任意数量的接口。结构也能实现接口。接口自己能够从多个基接口派生。
4.2.1 接口的基本概念
• 【本节示例参考:\示例代码\Chap04\TestInterface】
• 接口是类或结构的蓝图,即主要是对类或结构的构成进行限制,而具体的实现仍是由类或结构来具体实现的。
• 1.接口声明
• 接口的基本格式为:
• Interface-modifiers interface interface-name
• {
• Interface members //接口成员
• }
• 2.接口成员的定义
4.2.2 接口的继承
• 相似于类的继承性,接口也能够继承。接口继承的语法和类继承的语法相同。支持多继承表如今一个接口能够有多个基接口。基本格式:
• public Interface son-interface:father-interface,mother-inferface
• {
• Son-interface member //son-interface 接口的成员
• }
• 在上述定义中,接口要继承接口要用到冒号和接口名,若是继承多个接口,接口名先后列出,中间用逗号分割。接口son-interface继承了father-interface和mother-inferface接口。实现son-interface接口的类型必须实如今son-interface、father-interface和mother-interface接口中声明的成员。
4.2.3 接口的特色
• 接口具备本身独特的特色:
• 接口能够用任何可访问性来声明,可是接口成员必须全都是公共可访问性。即接口成员不能有任何访问修饰符。
• 不能使用static、virtual、abstract和sealed来定义。
• 接口没有构造函数。
• 接口中不容许定义字段。
4.3 属性和域
• 为了保存类的实例的各类数据信息,C#为咱们提供了两种方法——属性和域。属性实现了良好的数据封装和数据隐蔽。
4.3.1 域(field)
• 域表示与对象或类相关联的变量,它使类和结构能够封装数据。定义须要知足类的须要,并选用合适的数据类型。在有些资料和文档中也将其翻译为域。声明格式以下:
• attributes field-modifiers type field-name = value; //声明和初始化值类型
• attributes field-modifiers type field-name = new constructor_call //声明和初始化值引用类型或者结构
4.3.2 静态域和非静态域
• 静态域的声明是使用static修饰符,其余的域都是非静态域。静态域和非静态域分
• 别属于C#中静态变量和非静态变量。
• 若将一个域说明为静态的,不管创建多少个该类的实例,内存中只存在一个静态数据的复制。当这个类的第一个实例创建时,域被初始化之后再进行类的实例化时,再也不进行初始化,全部属于这个类的实例共享一个副本。
• 与之相反,非静态域在类的每次实例化时,每一个实例都拥有一份单独的复制。代码演示了静态域和非静态域的区别。
• 【本示例参考:\示例代码\Chap04\TestField2】
4.3.3 只读域
• 域的声明中若是加上了readonly 修饰符,代表该域为只读域。对于只读域咱们只能在域的定义中和它所属类的构造函数中进行修改,在其余状况下,域是“只读”的。
• 熟悉C和C++程序员可能习惯了使用const和#define定义一些容易记住的名字来表示某个数值static和readonly修饰符能够起到一样的效果:
• public class A
• {
• public static readonly double PI = 3.14159;
• public static readonly Color White = new Color(255, 255, 255);
• public static readonly int kByte = 1024;
• //other members
• }
4.3.4 域的初始化
• 在C和C++中,未经初始化的变量是不能使用的。在C#中,系统将为每一个未经初始化的变量提供一个默认值。这虽然在某种程度上保证了程序的安全性,但对本应初始化为某个特殊值的变量忘记了初始化,也经常会致使程序的执行误入歧途。
• 对于静态变量、非静态的对象变量和数组元素,这些变量自动初始化为自己的默认值。对于全部引用类型的变量默认值为null,全部值类型的变量的默认值如表所示。
4.3.5 属性(property)
• 属性是对现实世界中实体特征的抽象,它提供了对类或对象性质的访问。例如一个用户的姓名、一个文件的大小或一个窗口的标题,均可以做为属性。类的属性所描述的是状态信息,在类的某个实例中属性的值表示该对象的状态值。
• 在属性中提供了get和set访问器来对属性值进行读写,这就避免了直接操做属性值,充分体现了面向对象中的封装性。
• 属性能够是只读的或者可读写。咱们能够像访问或改变公有域那样,访问或改变属性的值。能够利用属性来容许从外部访问私有域的值。
4.3.6 访问属性的值
• 在属性的访问声明中,对属性的值的读操做用get关键字标出,对属性的值的写操做用set关键字标出。除了使用abstract修饰符的抽象属性,其余属性中get访问器都经过return来读取属性的值,set访问器都经过value来设置属性的值。
• 【本示例参考:\示例代码\Chap04\TestAttribute2】
4.4 索引器
• 索引器跟属性同样,都是为了以更直观的方式使用类。索引器在语法上比较简单,容许像访问数组那样来访问对象,即经过索引方式方便的访问类的数据信息的方法。
4.4.1 索引器的基本概念
• 【本节示例参考:\示例代码\Chap04\TestIndex1】
• 索引器声明的方式跟属性声明的方式很像。基本格式以下:
• Indexer-modifiers this parameter-list
• {
• Get
• {
• body of get accessor //对索引部分的读
• }
• Set
• {
• body of set accessor //对索引部分的写
• }
• }
4.4.2 索引器的使用
• 【本节示例参考:\示例代码\Chap04\TestIndex2】
• 在索引器中,若是只定义了一个get存取器,则索引器是只读的;若是只定义了一个set存取器,索引器能够是只写的;若是同时都定义了,则索引器就是可读写的。[]运算符用来调用索引器的get或set存取器,放在“[]”中参数必须对应索引器定义的参数列表,同时“[]”运算符的前面要加上类或结构实例的名称。下面来看一个例子如代码4-21所示,了解一下索引器是如何使用的。
4.4.3 索引器与属性的区别
4.5 迭代器(Iterator)
• 迭代器是开发人员可以在类或结构中遍历各自的数据结构的一种功能,能够用两种技术来实现,即foreach语句和迭代器。
4.5.1 foreach语句
• 【本节示例参考:\示例代码\Chap04\TestIterator1】
• foreach语句在目的上和for语句类似,都是用来迭代数组或另外集合的元素。若是要对一个类使用foreach,那么必须确保这个类提供了foreache所必须的GetEnumerator、MoveNext、Reset和Current成员。这些成员分别在IEnumerable和IEnumerator接口里定义。因此,只要实现了这两个接口就能够对其用foreach语句。IEnumerable接口声明一个方法支持对集合进行简单的遍历,的定义格式为:
• public interface IEnumerable
• 公有实例方法的定义以下:
• IEnumerable GetEnumerator()
4.5.2 迭代器
• 若是类要实现GetEnumerator、MoveNext、Reset和Current成员,才能与foreach兼容实现迭代,那就太麻烦了,因此C# 2008提供了关键字yield来实现迭代器。只要类中提供迭代器,便可遍历类中的数据结构。代码演示了如何使用迭代器。
• 【本示例参考:\示例代码\Chap04\TestIterator2】
4.6 小结
• 要理解面向对象,最重要的是要理解其中的一些基本概念,例如类和接口等。本节详细的介绍了面向对象的一些概念类、接口、属性和域和索引器。
• 虽然这些概念很难懂,可是它们倒是进入面向对象的第一步。要想学好面向对象编程,关键就要学好这一节。