06第2章 c#语言基础_第1页
06第2章 c#语言基础_第2页
06第2章 c#语言基础_第3页
06第2章 c#语言基础_第4页
06第2章 c#语言基础_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

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

文档简介

第3章C#语言基础,3,3.5类和结构,3.5.1定义类和结构,3.5.2定义属性,3.5.3定义索引器,3.5.4方法重载,3.5.5使用ref和out类型参数,3.5.6抽象类和接口,方法参数的功效就是能使信息在方法中传入或传出,当声明一个方法时,包含的参数说明是形式参数(形参)。当调用一个方法时,给出的对应实际参量是实在参数(实参),传入或传出就是在实参与形参之间发生的,在C#中实参与形参有四种传递方式。值参数引用参数输出参数参数数组,3.5.5使用ref和out类型参数,值参数在方法声明时不加修饰的形参就是值参数,它表明实参与形参之间按值传递。当这个方法被调用时,编译器为值参数分配存储单元,然后将对应的实参的值拷贝到形参中。实参可以是变量、常量、表达式,但要求其值的类型必须与形参声明的类型相同或者能够被隐式的转化为这种类型。这种传递方式的好处是在方法中对形参的修改不影响外部的实参,也就是说数据只能传入方法而不能从方法传出,所以值参数有时也被称为入参数。,3.5.5使用ref和out类型参数,值参数【例】下面的程序演示了当方法Sort传递的是值参数。usingSystem;classMyclasspublicvoidSort(intx,inty,intz)inttmp;/将x,y,z按从小到大排序if(xy)tmp=x;x=y;y=tmp;if(xz)tmp=x;x=z;z=tmp;if(yz)tmp=y;y=z;z=tmp;,3.5.5使用ref和out类型参数,值参数【例】下面的程序演示了当方法Sort传递的是值参数。classTeststaticvoidMain()Myclassm=newMyclass();inta,b,c;a=30;b=20;c=10;m.Sort(a,b,c);Console.WriteLine(a=0,b=1,c=2,a,b,c);Console.Read();,值传递.cs,3.5.5使用ref和out类型参数,引用参数引用参数与值参数不同,引用参数并不创建新的存储单元,它与方法调用中的实在参数变量同处一个存储单元。因此,在方法内对形参的修改就是对外部实参变量的修改。,3.5.5使用ref和out类型参数,引用参数【例】下面的程序演示了当方法Sort传递的是引用参数。usingSystem;classMyclasspublicvoidSort(refintx,refinty,refintz)inttmp;/将x,y,z按从小到大排序if(xy)tmp=x;x=y;y=tmp;if(xz)tmp=x;x=z;z=tmp;if(yz)tmp=y;y=z;z=tmp;,3.5.5使用ref和out类型参数,引用参数【例】下面的程序演示了当方法Sort传递的是引用值参数classTeststaticvoidMain()Myclassm=newMyclass();inta,b,c;a=30;b=20;c=10;m.Sort(refa,refb,refc);Console.WriteLine(a=0,b=1,c=2,a,b,c);Console.Read();,引用传递.cs,3.5.5使用ref和out类型参数,引用参数使用ref参数的注意点:(1)ref关键字仅对跟在它后面的参数有效,而不能应用于整个参数表。例如Sort方法中x、y、z都要加ref修饰。(2)在调用方法时,也用ref修饰实参变量,因为是引用参数,所以要求实参与形参的数据类型必须完全匹配,而且实参必须是变量,不能是常量或表达式。(3)在方法外,ref参数必须在调用之前明确赋值,在方法内,ref参数被视为初始值已赋过。,3.5.5使用ref和out类型参数,输出参数在参数前加out修饰符的被称为输出参数,它与ref参数很相似,只有一点除外,就是它只能用于从方法中传出值,而不能从方法调用处接受实参数据。在方法内out参数被认为是未赋过值的,所以在方法结束之前应该对out参数赋值。【例】下面的程序演示求一个数组元素中的最大值、最小值以及平均值。希望得到三个返回值,显然用方法的返回值不能解决,而且这三个值必须通过计算得到,初始值没有意义,所以解决方案可以定义三个out参数。,3.5.5使用ref和out类型参数,输出参数usingSystem;classMyclasspublicvoidMaxMinArray(inta,outintmax,outintmin,outdoubleavg)intsum;sum=max=min=a0;for(inti=1;imax)max=ai;if(aimin)min=ai;sum+=ai;avg=sum/a.Length;,3.5.5使用ref和out类型参数,输出参数classTeststaticvoidMain()Myclassm=newMyclass();intscore=87,89,56,90,100,75,64,45,80,84;intsmax,smin;doublesavg;m.MaxMinArray(score,outsmax,outsmin,outsavg);Console.Write(Max=0,Min=1,Avg=2,smax,smin,savg);Console.Read();,输出参数.cs,3.5.5使用ref和out类型参数,参数数组一般而言,调用方法时其实参必须与该方法声明的形参在类型和数量上相匹配,但有时候我们更希望灵活一些,能够给方法传递任意个数的参数,比如在三个数中找最大、最小和在5个数中找最大、最小甚或任意多个数中找最大、最小能使用同一个方法。C#提供了传递可变长度的参数表的机制,即使用params关键字来指定一个参数可变长的参数表。【例】下面程序演示了Myclass类中的方法MaxMin有一个参数数组类型的参数,那么在调用这个方法是所具有的灵活性。,3.5.5使用ref和out类型参数,参数数组usingSystem;classMyclasspublicvoidMaxMin(outintmax,outintmin,paramsinta)if(a.Length=0)/如果可变参数为零个,可以取一个约定值max=min=-1;return;max=min=a0;for(inti=1;imax)max=ai;if(aimin)min=ai;,3.5.5使用ref和out类型参数,参数数组classTeststaticvoidMain()Myclassm=newMyclass();intscore=87,89,56,90,100,75,64,45,80,84;intsmax,smin;m.MaxMin(outsmax,outsmin);/可变参数的个数可以是零个Console.WriteLine(Max=0,Min=1,smax,smin);m.MaxMin(outsmax,outsmin,45,76,89,90);Console.WriteLine(Max=0,Min=1,smax,smin);m.MaxMin(outsmax,outsmin,score);/可变参数也可接受数组对象Console.WriteLine(Max=0,Min=1,smax,smin);Console.Read();,参数数组.cs,3.5.5使用ref和out类型参数,从上面例程中可以看出设立可变参数非常方便,也很实用。但在使用时要注意以下几点:(1)一个方法中只能声明一个params参数,如果还要其他常规参数,则params参数应放在参数表的最后。(2)用params修饰符声明的参数是一个一维数组类型,例如,可以是int,string,double,或int,string等,但不能是int,string,等。(3)由于params参数其实是一个数组,所以在调用时可以为参数数组指定零个或多个参数,其中每个参数的类型都应与参数数组的元素类型相同或能隐式地转换。,3.5.5使用ref和out类型参数,(4)当调用具有params参数的方法时,可以作为一个元素列表如:(45,76,89,90);或作为一个数组(如:score)传递给params参数。(5)无论采用哪种方式来调用方法,params参数都是作为一个数组被处理。所以在方法内可以使用数组的长度属性来确定在每次调用中所传递参数的个数。(6)params参数在内部会进行数据的复制,不可能将params修饰符与ref和out修饰符组合起来用。所以在这个方法中即使对参数数组的元素进行了修改,在这个方法之外的数值也不会发生变化。,3.5.5使用ref和out类型参数,3.5.6抽象类和接口,到现在为止,我们使用的类都可以直接用来声明一个对象类型,并可以实例化。但C#中也可以有例外抽象类。这种类是为了需要而定义的一种非常基本的类,实际上并不想直接使用它,而是为了派生出新的类。抽象类是基类的一种特殊类型。除了拥有普通的类成员之外,还有抽象类成员。抽象类成员中的方法和属性,只有声明(使用关键字abstract),而没有实现部分。由于对实例而言,没有实现的成员是不合法的,所以抽象类永远也不能实例化。这种不能实例化的类也有它的作用空间,它们可以在类层次结构的上层,对于派生于该类的其他类而言,抽象类就确定了子类的基本结构和意义,从而使程序框架更容易建立。,包含一个或多个抽象函数的类本身必须声明为abstract,但是,抽象类可以包含非抽象的成员。从抽象类派生的类必须对基类中包含的所有抽象方法提供实现过程,否则,它也为抽象类。抽象函数为隐式的虚函数,所以为继承的抽象类提供了实现代码的方式与覆盖一个虚方法相似。另外,属性和索引也可以声明为abstract。抽象类不能自己实例化,需要使用其子类来实例化。抽象类是针对某些某些应用而产生的,抽象类的对象可以指向子类的实例,并且抽象方法可以被子类方法重写(override)。实例:抽象类.cs,3.5.6抽象类和接口,有时候,我们并不希望自己编写的类被继承,或者已经认定没有必要继承了。于是,C#提出了密封类的概念。类声明为密封后,就不能用来派生新的类。密封类具有不能用来继承的限制,但它也有自身的长处。一个类声明为密封的(sealed)有利于提高稳定性。因为,继承性是对基类的内部的某种程度的保护性访问。如果类是密封的,那么就完全避免了由派生类引起崩溃的可能性。同时,编译器也能针对密封类做相应的优化,例如,可以避免增加与虚拟方法相关联的系统总开销。实例:密封类.cs,3.5.6抽象类和接口,多继承在现实世界中是很普遍的,比如孩子会继承父母两方面的特征等。C#中的类和抽象类都不能进行多继承,而通过接口可以实现多继承。接口很像类,但是接口中不能包含任何数据成员,接口中可以包含方法、属性、事件和索引器等的声明,但是接口本身不提供对它所声明的成员的实现。类通过“属性”和“行为”来描述事物,而接口只能包含行为的描述。比如我们可以把电器的“开”和“关”行为单独定义为一个接口,这样凡是继承自这个接口的电器都有了“开”和“关”这两个方法,可以让电灯、电视等继承并实现接口。,3.5.6抽象类和接口,1)接口的定义C#接口的定义如:publicinterfaceISwitchvoidOn();voidOff();接口的定义与类的定义类似,使用interface定义代替class。另外,接口里面的方法成员不能有方法实现(与抽象类类似)。接口的命名通常是以I开头,如IPartA、IPartB。接口不能被实例化。所有接口成员都不包含访问修饰符,所有成员默认是public。,3.5.6抽象类和接口,2)接口的实现接口自身并不包含任何实现代码,类可以继承自接口,继承了以后就必须实现接口里面的方法。publicclassLight:ISwitchprivatestringid;publicLight(string_id)this.id=_id;publicvoidOn()Console.WriteLine(电灯开了);publicvoidOff()Console.WriteLine(电灯关了);实例:接口.cs,3.5.6抽象类和接口,接口和接口之间可以互相继承,并且允许多重继承:interfaceIAvoidPlayA();interfaceIBvoidPlayB();interfaceIC:IA,IB,3.5.6抽象类和接口,一个类可以同时继承自类、抽象类和接口。类和抽象类要写在前面,接口写在后面。classClassB:ClassA,IA,IB抽象类和接口在很多方面具有很大的相似性,抽象类和接口都是抽象体,但是其区别体现在以下两个方面:1、接口中不定义任何与实现有关的内容;2、抽象类是实体的抽象,接口则是行为的抽象。,3.5.6抽象类和接口,3)显式接口实现一个类可以同时继承自多个接口,如果两个接口中有同名的方法,就要用显式接口来实现。显式接口的实现语法注意以下两个方面:1、在实现的方法上带上接口名;2、显式接口方法访问修饰符不能为public类型(否则对象在调用方法时不知道调用那个接口的方法)。实例:显式接口.cs,3.5.6抽象类和接口,3.2C#的数据类型,C#的数据类型分为值类型和引用类型两大类。值类型包括整数类型、布尔类型、实数类型、字符类型、结构类型和枚举类型等。值类型被分配在堆栈上,值类型的变量直接包含了数据,可以直接访问其值;引用类型包括类类型、数组类型、代理类型、接口类型等。引用类型总是分配在托管堆上,引用类型的变量通常仅包含一个指向实例的指针,系统通过该指针来引用其实例。,代理(委托)类型,通过参数把数据传递给方法是我们习惯的一种做法,那么如何把方法作为参数传递给另一个方法?C#的代理相当于在C/C+中的函数指针。函数指针用指针获取一个函数的入口地址,实现对函数的操作。代理与C/C+中的函数指针不同在于代理是面向对象的,是引用类型,因此对代理的使用要先定义后实例化,最后才调用。代理指定了方法的返回类型和形式参数类型,但没有指定具体的实现过程,只要这些方法与代理的签名(返回类型及形式参数)相同。同一个代理在运行期间可以表示不同的方法实现过程。只是在运行时,为代理赋一个适当的方法,当调用此代理时,它将执行此方法实际所封装的代码。,代理是函数的封装,它代表一委托是函数的封装,它代表一“类”函数。它们都符合一定的签名:拥有相同的参数列表、返回值类型。同时,委托也可以看成是对函数的抽象,是函数的“类”。此时,委托的实例将代表一个具体的函数。为什么要使用委托?1、更加灵活的方法调用;2、用于异步回调;3、多线程编程中使用委托来指定启动一个线程时调用的方法;4、C#中的事件模型。用它们指明处理给定事件中的方法,代理(委托)类型,1)代理的定义代理定义了方法的返回类型和参数类型,也创建了一种新类型,代理也是一个类,它派生于基类System.Delegate。和其他类一样,必须首先定义代理,然后才能实例化。代理定义的语法:修饰符delegate返回值类型(参数);例如:delegateintMyDelegate(intnID,stringsName);定义了一个名称为MyDelegate的代理,这个代理代表的方法带有两个参数,返回一个整型数。,代理(委托)类型,2)代理的实例化定义了代理之后,必须要实例化代理,还需要代理的方法处理程序,这个方法实现具体的功能,其参数列表必须相同,并且必须返回同样的类型。然后再将这个方法赋给代理对象。定义:delegateintMyDelegate(intnID,stringsName);实例化:MyDelegated1=newMyDelegate(wr.InstanceMethod);,代理(委托)类型,3)通过代理调用方法通过代理的定义和实例化工作后,我们就可以使用代理了。使用代理就好像它就是代理的方法本身一样,和直接使用方法的格式相同。我们可以通过下面的语句来调用代理:d1(5,“aaa”);通过代理MyDelegate实现对方法InstanceMethod的调用,调用还必须有一个前提条件是:方法InstanceMethod的参数和定义MyDelegate的参数一致,并且返回值为int。方法InstanceMethod定义:publicintInstanceMethod(intnID,stringsName)代理.cs,代理(委托)类型,4)多播委托多播委托允许将多个委托对象组成一个委托链(比如某个事件造成连锁反应)。委托链可以承载很多方法,一旦触发该链,那么将调用链中所有的方法。可以使用“+”把委托对象加入委托,使用“-”从委托链中移出一个委托对象。实例:多播委托.cs注意:1.多播委托的委托方法一般是没有返回值的方法(void)2.多播委托只支持“+”、“-”、“+=”、“-=”。,代理(委托)类型,(1)类的字段成员的定义,(2)类的方法的定义,(3)类的属性的定义,(4)类的索引器的定义,类的成员的定义,(5)类的事件的定义,事件是Windows编程中很重要的一个概念,事件可以由用户的操作触发,也可能由程序逻辑触发。事件最常见的用途是用于窗体编程,当发生像点击按钮、移动鼠标等事件时,相应的程序将收到通知,再执行代码。事件作为C#中类的一种成员,为类和类的实例定义发出通知的能力,从而将事件和可执行代码捆绑在了一起。事件的发布者定义和发布事件,事件的接收者接收事件并调用自己的方法处理事件,这就是计算机中事件机制的运行原理(类似报纸订阅)。,(5)类的事件的定义,1)事件的定义在C#中,事件实际上是代理的一种特殊形式。事件是为处理过程特制的、更为专业化的代理,因此,事件比普通的代理更易于使用,更强健。下面是定义

温馨提示

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

评论

0/150

提交评论