C#面向对象编程_第1页
C#面向对象编程_第2页
C#面向对象编程_第3页
C#面向对象编程_第4页
C#面向对象编程_第5页
已阅读5页,还剩50页未读 继续免费阅读

下载本文档

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

文档简介

5.1继承

继承是重要的代码重用技术,更重要的继承反映了许多现实世界问题的内在特点。

C#中的继承只是单继承,C#不支持多继承机制,因为尽管多继承带来许多灵活性,但也会引发一些不确定性,给编程和系统运行带来麻烦。

5.5.1继承的定义在现有的类(称为直接基类)的基础上建立新类(称为派生类)的处理过程称为继承。在创建新类的过程中,直接基类不作任何改变,派生类继承其直接基类的成员。继承意味着派生类隐式包含它的直接基类的所有成员,但不能继承基类的实例构造函数、静态构造函数、析构函数。通常,继承是指显式继承,由于C#只支持单继承,因此基类只有一个。继承(续)继承(续)从基类继承或派生出一个子类的语法格式为:class派生类名:基类名{派生类代码}

其中,冒号“:”的含义是“派生于”。在C#面向对象编程语言中,类的继承机制有以下特征:(1)派生类直接继承其基类的成员,并隐式包含其直接基类中除构造函数和析构函数外的所有成员。

继承(续)

(2)继承具有可传递性。如果A类派生出B类,B类派生出C类,则C类会继承A类和B类中所有的类成员(不含构造函数和析构函数)。(3)派生类可在其基类的基础上添加新成员,但不能删除所继承的成员定义。(4)在派生类中,可以通过定义与所继承的基类成员具有相同名称的新成员来隐藏所继承的成员,从而使所继承的成员在派生类的实例中不可访问。(5)派生类与其基类之间存在隐式转换关系,因此,对派生类的实例引用可以看作是对其基类的实例引用。【例5-1】类继承时隐藏同名成员的应用示例。usingSystem;namespaceSchool{ publicclassStudent { protectedstringname; protectedintid; publicStudent() {} publicStudent(stringstr,int_id) { name=str; id=_id; }

继承(续)

继承(续)

publicvoidPrintBase(){Console.WriteLine("在基类:Student."); } publicvoidFindInfo(int_id) { if(_id==id) Console.WriteLine("{0}isastudent.",Name); } }publicclassCollegeStudent:Student {stringdepartment;newstring_name;//隐藏同名字段成员

继承(续)

publicCollegeStudent(stringdept,stringstr,int_id) { department=dept; name=str; id=_id; }publicvoidPrintDerived() {Console.WriteLine("在派生类:CollegeStudent."); } publicnewvoidFindInfo(int_id)//隐藏同名方法

{ if(_id==id) Console.WriteLine("{0}isastudentinthe{1}Department.",name,department); } }

继承(续)publicclassTest{publicstaticvoidMain() {Studentstudent=newStudent("李四",0518277);CollegeStudentcollegeStudent=newCollegeStudent(“计算机”,“张三",0518288); student.FindInfo(0518277); student.PrintBase(); collegeStudent.FindInfo(0518288); collegeStudent.PrintBase(); collegeStudent.PrintDerived(); }}}

继承(续)

派生类继承基类的成员,有利于减少维护的工作量。在实际的软件项目开发中,许多子程序和算法都被反复使用,对常用类库的需求很大,因此,一些软件公司开发出了很多类库。.NET中提供了一个C#程序设计使用的类库,其中System.Object是最基本的类,所有其他的类都直接或间接地派生于这个类,派生出来的类称为子类,被继承的类称为父类。因此,通过继承,类库中的类组成了一个树状层次结构。

继承(续)

5.1.2重写

重写方法用相同的签名重写所继承的虚拟方法。最常用的重写就是对接口方法的实现。接口中一般只是对方法进行了声明,而在使用接口时,则需要实现接口中声明的所有方法。

5.2抽象和密封

抽象类和密封类是C#中两个特殊的类类型。抽象类用于提供多个派生类可共享的基类的公共定义,密封类用于控制类的派生。5.2.1抽象类从许多事物中舍弃个别的、非本质的特征,而抽取共同的、本质性的特征,就是抽象的过程。通过使用抽象,分析员不需要了解和描述复杂的问题域中的所有事物,而只需要分析、研究其中与系统目标有关的事物及其本质特征,通过舍弃个体事物在细节上的差异,并抽取这些事物的共同特征,从而可以得到事物的抽象概念。抽象和密封(续)

抽象是面向对象方法中使用最广泛的原则。抽象包括过程抽象和数据抽象两个方面。过程抽象是指对于任何一个完成确定功能的操作序列,使用者都可以把它看作是一个单一的实体,尽管实际上它可能是由一系列更低级的操作完成的。数据抽象是指根据施加于数据之上的操作来定义数据类型,并限定数据的值只能由这些操作来修正。数据抽象是面向对象分析的核心原则,它强调把数据(属性)和操作(服务)结合为一个不可分的系统单位(对象),对象的外部只需要知道它做什么,而不必知道如何做。抽象和密封(续)

抽象类是基类的一种特殊类型,它除了拥有普通的类成员之外,还具有抽象类成员。抽象类中的方法和属性只有声明(使用关键字abstract),而没有实现部分。由于对实例而言,没有实现的成员是不合法的,因此,抽象类不能实例化。抽象类既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法,因此,不能对抽象类使用new关键字,也不能将其密封。如果派生类没有实现所有的抽象方法,则此派生类也必须声明为抽象类。另外,在派生类实现抽象方法时,应对此方法用修饰符override加以修饰。

抽象和密封(续)5.2.2密封类

如果所有的类都可以被继承,就很容易导致继承的滥用,使类的层次结构变得复杂,从而使开发人员对类的理解和使用变得困难,为了避免这种情况,C#提出了密封类的概念。密封可以用来限制类的扩展和继承,如果密封了某个类,则其他类不能从此类继承;如果密封了某个成员,则派生类不能对此成员进行重写操作,即密封可以防止对类的成员进行继承或重写。在默认情况下,不应密封类的成员。抽象和密封(续)在C#中使用的类如果满足如下条件,则应将其密封:(1)类是静态类。(2)类包含带有安全敏感信息的受保护成员。(3)类继承多个虚成员,并且密封每个成员的开发和测试的开销明显大于密封整个类。(4)类是一个要求使用反射(一种动态程序的组织和管理技术)进行快速搜索的属性。密封属性可提高反射在检索属性时的性能。抽象和密封(续)

在C#中声明抽象类时需要使用sealed关键字,语法格式如下:访问修饰符sealedclass类名:基类或接口{类成员}说明:(1)密封类不能作为基类被继承,但它可以继承其他类或接口。(2)在密封类中不能声明受保护成员或虚成员,因为受保护成员只能从派生类进行访问,而虚成员只能在派生类中重写。(3)密封类的不可继承性使其不能声明为抽象的,即sealed修饰符不能与abstract修饰符同时使用。

5.3多态

多态性是一项允许将父对象设置为一个或多个子对象相等的技术,引用之后,父对象就可以动态地根据当前引用的子对象的特性以不同的方式运作。也可以这样理解,多态就是“多种形式”,它意味着可以利用动态绑定技术,用具有相同名称的方法来调用方法的不同具体实现。多态性是面向对象的程序设计语言的基本特征之一。

多态(续)

5.3.1多态定义

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。多态性通过派生类重载基类中的虚函数来实现,虚函数一般virtual来修饰。多态(续)C#支持两种类型的多态性:(1)编译时的多态性:编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时根据传递的参数、返回值的类型等信息决定实现何种操作。(2)运行时的多态性:运行时的多态性是指直到系统运行时,才根据实际情况决定实现何种操作。在C#中,运行时的多态性通过虚成员实现。

多态(续)5.3.2多态性应用

多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。多态性允许对类的某个方法进行调用,而无需考虑此方法所提供的特定实现。例如,名为Road的类调用另一个类的Drive方法,这“另一个类”可能是SportsCar或SmallCar,但两者都提供Drive方法。虽然Drive方法的实现因类的不同而异,但Road类仍可以调用它,其提供的结果可由Road类使用和解释。多态(续)1.接口的多态性应用接口本质上是类需要如何响应的定义,它描述了类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,但将这些成员的特定实现由派生类完成。组件编程中的一项强大技术就是能够在一个对象上实现多个接口,每个接口由一些紧密联系的方法、属性和事件组成。组件可以通过接口为其他组件提供功能,开发人员最常使用的组件功能是组件类本身的成员。然而,包含大量成员的组件使用起来可能比较困难,可以考虑将组件的某些功能分解出来,作为单独实现的接口。多态(续)

可以通过定义和实现附加接口增量地将功能添加到组件中,其优点是:(1)简化了设计过程,需要增加功能时,只需在组件中添加实现这些功能的方法即可。(2)简化了兼容性的维护,组件的新版本可以在添加新接口的同时继续提供现有接口。2.继承的多态性应用多个不同的类可以从单个基类继承。通过继承,类可以接收基类的所有方法、属性和事件,从而可以根据需要来实现附加成员,并可以重写基成员以提供不同的实现。多态(续)

C#通过继承提供多态性,对于小规模开发任务而言,这是一个功能强大的机制,但对于大规模系统,通常会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的开发时间没有任何帮助。使用继承首先是为了向现有基类添加功能,若从经过完全调试的基类框架开始,将方法增量地添加到基类而不会中断版本,则程序员的工作效率将大大提高。多态(续)3.抽象类的多态性应用抽象类本身不能实例化,它必须被继承,其部分或全部成员的实现由继承类提供。已实现的成员仍可被重写。当需要一组相关组件包含一组具有相同功能的方法,但同时要求在其他方法实现中具有灵活性时,可以使用抽象类。需要进行版本时,也可以使用抽象类。

5.4泛型

泛型可以使类、结构、接口、委托和方法通过它们存储和操纵的数据的类型被参数化。软件设计中涉及很多算法和数据结构,泛型的主要思想就是将算法和数据结构完全分离开来,使一次定义的算法能够作用于多种数据结构,从而实现代码的高度可重用性。

泛型(续)

5.4.1泛型的引入

2005年底微软公司正式发布了C#2.0,与C#1.x相比,这一版本增加了很多新特性,其中最重要的是对泛型的支持,可以通过泛型定义类型安全的数据结构,而无须使用实际的数据类型,这能得到更高质量的代码并显著提高系统性能。泛型在功能上类似于C++的模板。泛型(续)

在编写程序时,经常遇到两个模块的功能非常相似,如一个模块用于处理int类型的数据,而另一个则用于处理string类型的数据,但方法的参数类型不同使得程序设计人员只能分别编写多个不同的方法处理每个数据类型。如果可以在方法中传入通用的数据类型,就会解决这一问题,这就是泛型。所谓泛型,即通过参数化类型来实现在同一方法中操作多种数据类型。例如,下面的代码的功能是实现一个栈,这个栈只能处理int数据类型。

泛型(续)5.4.2泛型的创建和使用

1.创建泛型泛型提供了一种新的创建类型的机制,使用泛型创建的类型将带有类型形参。下面的示例声明了一个带有类型形参T的泛型Stack类,其中类型形参在<>分隔符中指定并放置在类名后。Stack<T>的实例的类型由创建时所指定的类型确定,实例将存储这个类型的数据而不进行数据类型转换。类型形参T只起占位符的作用,直到在使用时为其指定了实际类型。例如:泛型(续)public

class

Stack<T>{

T[]

items;

int

count;

public

void

Push(T

item){}public

T

Pop()

{}}泛型(续)2.泛型类用泛型来重写上述示例中的栈,用一个通用的数据类型T来作为一个占位符,等待在实例化时用一个实际的数据类型来代替。

publicclassStack<T>{

privateT[]m_item;

publicTPop(){}

publicvoidPush(Titem){}

publicStack(inti)

{

this.m_item=newT[i];

}}泛型(续)3.泛型方法泛型不仅能作用于类,也可以单独用在类的方法上,并可以根据方法参数的类型自动适应各种参数,这样的方法即为泛型方法。5.4.3泛型类的成员1.泛型类中的静态成员变量2.泛型类中的静态结构函数3.重载泛型类中的方法4.重写泛型类的方法

5.5接口

接口描述的是可属于任何类或结构的一组相关功能。实现接口的类或结构要与接口的定义严格一致。接口可以从多个基接口继承,但不能被实例化。接口好比一个模版,这个模版定义了对象必须实现的方法。

接口(续)5.5.1定义接口

接口是包含一组虚方法的抽象类型,其中每一种方法都有名称、参数和返回值。接口方法不能包含任何实现,CLR允许接口包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数,但不能包含任何实例成员。一个类可以实现多个接口,当一个类继承某个接口时,它不仅要实现这个接口定义的所有方法,还要实现此接口从其他接口中继承的所有方法。接口(续)下面给出一个接口定义示例:publicinterfaceComparable{intCompareTo(objecto);}

接口(续)抽象类和接口的区别:(1)类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类。接口只是一个行为的规范或规定。(2)抽象类型可以选择提供实现的详细信息;而接口不能提供实现的任何详细信息。(3)抽象类不能被密封。(4)抽象类实现的具体方法默认为虚方法,但实现接口的类中的接口方法默认为非虚。(5)好的接口定义应该具有专一的功能,而不是具有多功能,否则会造成接口污染。接口污染是指一个类实现了一个接口中的某个方法,就必须实现该接口中的其他方法接口(续)

(6)如果用抽象类实现接口,则可以把接口中的方法映射到抽象类中作为抽象方法,不必进行具体实现,而是在抽象类的子类中实现接口中的方法。(7)如果预计要创建组件的多个版本,则使用抽象类。抽象类通过提供简单的方法来控制组件版本。(8)如果要设计小而简练的功能模块,则使用接口;如果设计大的功能单元,则使用抽象类。(9)如果要在组件的所有实现之间提供通用的已实现功能,则使用抽象类。(10)抽象类主要用于关系密切的对象,而接口适用于为不相关的类提供通用功能。5.5.2实现接口

5.6迭代器

迭代器是可以产生值的有序序列的一段代码,通过它能按照指定的顺序来访问一个集合中的所有元素。5.6.1定义迭代器迭代器是C#2.0中的新功能。迭代器使程序能够在类或结构中支持foreach迭代,而不必实现整个IEnumerable接口。只需提供一个迭代器,即可遍历类中的数据结构。当编译器检测到迭代器时,它将自动生成IEnumerable或IEnumerable<T>接口的Current、MoveNext和Dispose方法。迭代器(续)迭代器具有以下特点:(1)迭代器可用作方法、运算符或get访问器的代码。(2)迭代器代码使用yieldreturn语句依次返回每个元素,使用yieldbreak语句则可以终止迭代。其中,yield关键字用于指定返回的值。(3)可以在类中实现多个迭代器,每个迭代器都必须像类的成员一样有唯一的名称,并可以在foreach语句中被客户端代码调用,如“foreach(intxinSampleClass.Iterator2){}”。(4)迭代器的返回类型必须为IEnumerable、IEnumerator、IEnumerable<T>或IEnumerator<T>。

迭代器(续)

5.6.2实现迭代器

创建迭代器最常用的方法是对IEnumerable接口实现GetEnumerator方法。例如:

publicSystem.Collections.IEnumeratorGetEnumerator(){for(intj=0;j<max;j++){yieldreturnj;}}迭代器(续)下面给出一个创建和使用迭代器的示例。//实现迭代器的类publicclassYear:System.Collections.IEnumerable{string[]season={"Spring","Summer","Autumn","Winter"};publicSystem.Collections.IEnumeratorGetEnumerator(){for(inti=0;i<season.Length;i++){yieldreturnseason[i];}}}迭代器(续)classProgram{staticvoidMain(string[]args){Yeary=newYear();//使用迭代器

foreach(stringsiny){System.Console.WriteLine(s+"\n");}}}5.7委托

委托是C#特有的成分,又被翻译为代理、代表等。委托的功能类似于C或C++中的函数指针,但与函数指针相比,委托能保持C#的面向对象和类型安全的先进功能。委托可以将具有相同参数列表和返回类型的方法组织起来,以实现方法回调和事件处理。5.7.1定义委托

委托是一种引用方法的类型。一旦为委托分配了方法,委托将与此方法具有完全相同的行为。与其他方法一样,委托方法具有参数和返回值,例如:publicdelegateintPerformCalculation(intx,inty);

委托(续)

与委托签名(由返回类型和参数组成)匹配的任何方法都可以分配给这个委托,这样就可以通过编程的方式来更改方法调用,还可以向现有的类中插入新代码。将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。例如,可以向排序算法传递对比较两个对象的方法的引用。

委托(续)

5.7.2实例化委托例如:

publicdelegatevoidDel(stringmessage);publicclassExample{Deldel;//实例化委托

publicvoidmethod(stringmessage){

……}del=newDel(method);}

委托(续)

5.7.3多重委托多重委托是由两个或更多的其他委托构成的委托。当调用委托时,可以调用多个方法,这又称为多路广播。若要向委托的方法列表(调用列表)中添加额外的方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。5.7.4委托作为参数和返回值5.8匿名方法

构造委托对象时,如果使用匿名方法,则可以直接将方法的代码块作为参数传给委托而不必调用方法,因此减少了实例化委托所需的编码系统开销。匿名方法表达式由3部分组成,即关键字delegate、参数列表以及执行代码段,其中只有参数列表可以省略。其语法格式如下:delegate{参数列表}{

执行代码段}匿名方法(续)

匿名方法表达式本身并不是一个完整的语句,只有将它赋值给一个委托对象才能构成一个语句。在赋值给某个委托对象时,要求匿名方法表达式与委托的定义保持一致,具体要求如下:(1)匿名方法表达式可以省略参数列表,其对应的委托类型可以包含任意多个参数,但不能包含输出参数,而且不能在代码段中使用这些参数。(2)如果匿名方法表达式指定了参数列表,则参数数量和参数类型必须和委托中定义的一致,或者参数类型可以隐式地转换为委托中对应的参数类型。匿名方法(续)

(3)匿名表达式的返回类型(即其执行代码中return语句中的返回类型)必须和委托定义的返回类型相同或可以隐式地转换为委托的返回类型。如果存在多个return语句,则要求每个语句的返回类型相同,并与委托的返回类型相同。(4)如果委托定义的返回类型是void,那么执行代码段没有return语句,或是单独使用不带任何表达式的return语句。

5.9事件

事件是类在发生其关注的事情时用来提供通知的一种方式,通常使用委托事件处理程序进行声明,并可以调用匿名方法来替代委托。5.9.1定义事件在C#中,事件实际上是委托的一种特殊形式,事件是专门用于处理过程的更为专业化的委托。定义事件的语法格式如下:

[访问修饰符]e

温馨提示

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

评论

0/150

提交评论