第二章抽象类与接口Word格式.docx
- 文档编号:16791635
- 上传时间:2022-11-26
- 格式:DOCX
- 页数:15
- 大小:50.88KB
第二章抽象类与接口Word格式.docx
《第二章抽象类与接口Word格式.docx》由会员分享,可在线阅读,更多相关《第二章抽象类与接口Word格式.docx(15页珍藏版)》请在冰豆网上搜索。
classApple:
Fruit//苹果
publicoverridevoidGrowInArea()
{
Console.WriteLine("
南方北方都可以种植我。
"
);
}
classPineapple:
Fruit//菠萝
我喜欢温暖,只能在南方看到我。
注意上述代码中的override关键字,这说明子类重写了基类的抽象方法。
抽象类不能创建对象,一般用它来引用子类对象。
Fruitf;
f=newApple();
f.GrowInArea();
f=newPineapple();
运行结果:
注意:
同一句代码“f.GrowInArea();
”会由于f所引用的对象不同而输出不同的结果。
可以看到,代码运行结果类似于4.1.3节介绍的“虚方法调用”特性,两者没有本质差别。
可以按照以下格式编写代码:
抽象类抽象类变量名=new继承自此抽象类的具体子类名();
一个抽象类中可以包含非抽象的方法和字段。
因此:
包含抽象方法的类一定是抽象类,但抽象类中的方法不一定是抽象方法。
在C#中,抽象方法只能出现在抽象类内(即在普通类中不能定义一个抽象方法),而一个抽象类可以没有一个抽象方法(即它的所有方法都不是抽象的)。
这些像绕口令式的结论不用死记,因为在编译程序时,C#编译器会自动帮我们检测代码,并给出详细的提示信息。
实现了抽象基类中所有抽象方法的类称为“ConcreteClass”,汉语不太好翻译,直译为“具体类”,但笔者认为意译为“可实例化类”更贴切,因为这种类的最大特点之一就是可以使用它来创建对象。
2.抽象属性
除了方法可以是抽象的之外,属性也可以是抽象的,请看以下代码。
abstractclassParent
publicabstractStringMessage
//抽象属性
get;
set;
classChild:
Parent
privateString_msg;
publicoverride
StringMessage
get
return_msg;
set
_msg=value;
使用代码如下:
Parentp=newChild();
p.Message="
Hello"
;
3.密封类
抽象类一般作为基类而存在,没有任何子类的抽象类是无意义的。
但在C#中,却有一种特殊的类不能拥有任何子类,这就是是密封类。
在一个类前面加上“sealed”关键字,就成了密封类。
sealedclassCannotInherit
除了不能作为基类,密封类的使用与普通类没有什么差别。
4.2.2
接口
我们来看下面这句话:
鸭子是一种鸟,会游泳,同时又是一种食物。
如何在面向对象的程序中表达这种关系?
如果使用C++,可以设计成让鸭子(Duck)类继承自两个父类(鸟Bird和食物Food)。
但在C#中,所有的类都只能有一个父类,此方法不可行。
为了解决这一问题,C#引入了接口(Interface)这一概念,并规定“一个类可以实现多个接口”。
1.接口的定义与使用
关键字interface用于定义接口(示例项目InterfaceExamples)。
//定义两个接口
publicinterfaceISwim
voidSwim();
publicinterfaceIFood
voidCook();
接口可以看成是一种“纯”的抽象类,它的所有方法都是抽象方法。
接口的成员默认就是public的,不要画蛇添足地在方法前加上“public”关键字。
可以用与继承相同的语法定义一个类实现某些接口。
//定义一个抽象类
publicabstractclassBird
publicabstractvoidFly();
//继承自一个抽象类,实现两个接口
publicclassDuck:
Bird,IFood,ISwim
//实现ISwim接口
public
voidSwim()
是鸭子就会游泳"
//实现IFood接口
publicvoidCook()
鸭子经常被烧烤,北京烤鸭就很有名"
//实现抽象类Bird中的抽象方法
publicoverridevoidFly()
只有野鸭才会飞"
上述代码可以图示为图4-8。
图4-8
实现接口
可以看到,抽象类定义了对象所属的类别,而接口实际上定义了一种对象应具有的行为特性。
若一个类同时实现多个接口和派生自一个基类,定义时,基类要放在第一位。
可按以下格式使用接口:
接口类型名变量名=new实现了接口的类型名();
示例代码如下:
staticvoidMain(string[]args)
Duckd=newDuck();
//Duck对象d可以使用3种类型的方法:
//1.自身定义的
//2.父类定义的
//3.接口定义的
d.Fly();
d.Cook();
d.Swim();
//将子类(Duck)对象赋给基类变量
Birdb=d;
//现在只能使用基类定义的Fly()方法
b.Fly();
//将Duck对象赋给ISwin接口变量
ISwims=d;
//现在只能使用接口定义的Swim()方法
s.Swim();
//将Duck对象赋给另一个实现的接口IFood变量
IFoodf=d;
//现在只能使用接口定义的Cook()方法
f.Cook();
请读者仔细地阅读上述代码的注释,由于Duck类继承自抽象基类Bird,又实现了ISwim和IFood两个接口,所以,Duck对象拥有这三者所定义的所有方法,并且可以赋值给这三种类型的变量。
需要注意的是,虽然程序中始终都只有一个Duck对象,但将其赋值给不同类型的变量后,其可以使用的方法是不一样的。
2.显式实现接口
上面讲到,某个类可以实现多个接口,当创建一个此类的对象之后,通过引用这个对象的对象变量可以访问其所有的公有方法。
请看以下代码:
interfaceIMyInterface
voidfunc();
publicclassA:
IMyInterface
publicvoidfunc()
//……
publicvoidfunc2()
上述代码定义了一个接口IMyInterface,其中定义了一个func()方法,类A实现了这一方法,同时定义了另一个func2()方法。
当创建一个A对象后,现在可以同时使用func()和func2()两个方法。
Aa=newA();
a.func();
a.func2();
在这种情况下,根本分不清哪些方法是由接口定义的,哪些是由类自己定义的。
C#提供了一种“显式接口”实现机制,可以区分开这两种情况,其代码如下:
voidIMyInterface.func()
请注意在方法func前以粗体突出显示的接口名称,这就是C#对接口IMyInterface的显式实现方式。
同时,不再需要public关键字。
当类A显式实现接口IMyInterface之后,以下代码就无法通过编译。
C#编译器会报告:
“OnlyTest.A”并不包含“func”的定义
上述结果中的“OnlyTest”是一个C#示例项目的名称,在实际开发时会被替换为您的C#项目名称。
只有像下面这样修改代码,才可以通过编译。
IMyInterface
a=newA();
由此得到一个结论:
如果一个类显式实现某个接口,则只能以此接口类型的变量为媒介调用此接口所定义的方法,而不允许通过类的对象变量直接调用。
或者这样说:
被显式实现的接口方法只能通过接口变量访问,而不能通过类实例直接访问。
3.接口的合并
接口实际上是一种特殊的类型,有意思的是,接口还可以“实现”接口。
interfaceOne
voidf1();
interfaceTwo
voidf2();
//实现了两个接口的接口
interfaceThree:
One,Two
voidf3();
实现Three接口的类必须同时实现3个方法f1()、f2()和f3()。
classSomeClass:
Three
publicvoidf3()
{
//……
publicvoidf1()
publicvoidf2()
接口其实是一个特殊的CLR类型,没有基类型,因此,一个接口不能“继承”另一个接口。
但说一个接口“实现”另外一些接口也是不准确的,它只是在语法上采用了与类实现接口相同的形式,笔者认为,更确切的词应该是“合并”。
接口的“合并”在实践中用得并不多。
4.接口应用实例——回调
接口在面向对象编程中应用极广。
回调(CallBack)就是一个典型的示例。
先解释一下回调的概念。
通常情况下,我们创建一个对象,并马上直接调用它的方法。
然而,在有些情况下,希望能在某个场景出现后或条件满足时才调用此对象的方法。
回调就可以解决这个“延迟调用对象方法”的问题。
这个被调用方法的对象称为回调对象。
实现回调的原理简介如下:
首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象。
控制器对象负责检查某个场景是否出现或某个条件是否满足。
当此场景出现或此条件满足时,自动调用回调对象的方法。
可以举个现实生活中的例子。
一读者想借《编程的奥秘——.NET软件技术学习与实践》这本书,但这本书已被其他读者借走了。
于是,读者与图书馆管理员间发生了以下对话:
读者:
“我把我的电话号码告诉你,等书一到就马上通知我。
”
管理员:
“好的。
另一读者把书还回来后,马上给您打电话,书我先帮您留着。
在上述这个场景中,读者就是“回调对象”,管理员就是“控制器对象”,读者的电话号码就是“回调对象的方法”,另一读者的还书事件就是“某一特定的场景”。
本节示例CallBackExamples展示了如何使用接口实现回调,此示例运行截图如图4-9所示。
图4-9
回调示例
程序运行时,从键盘上按任意一个键显示当前时间,整个程序可以“没完没了”地运行下去,除非您按了Esc键。
示例项目CallBackExamples的类图如图4-10所示。
ICallBack接口定义了一个run()方法。
//实现回调的类必须实现此接口
publicinterfaceICallBack
voidrun();
CallBackClass类实现此接口,并在其run()方法中向控制台窗口输出当前时间。
图4-10
CallBackExamples示例项目类图
classCallBackClass:
ICallBack
publicvoidrun()
//输出当前时间
System.Console.WriteLine(DateTime.Now);
Controller类中有一个私有的ICallBack类型的字段,用于存放回调对象的引用,此对象引用在构造函数中传入。
classController
publicICallBackCallBackObject=null;
//引用回调对象
publicController(ICallBackobj)
this.CallBackObject=obj;
publicvoidBegin()
敲任意键显示当前时间,ESC键退出...."
while(Console.ReadKey(true).Key!
=ConsoleKey.Escape)
CallBackObject.run();
Controller类的Begin()方法启动整个处理过程。
调用代码如下:
classProgram
staticvoidMain(string[]args)
//创建控制器对象,将提供给它的回调对象传入
Controllerobj=newController(newCallBackClass());
//启动控制器对象运行
obj.Begin();
可以看到,当示例程序运行时,何时调用CallBackClass对象的的run()方法是由用户决定的,用户每敲一个键,控件器对象就调用一次CallBackClass对象的的run()方法。
在这个示例中,实现回调的关键在于ICallBack接口的引入。
读者可能在想,如果不用ICallBack接口,而直接使用CallBackClass对象,也可以实现同样的运行效果。
publicCallBackClassCallBackObject=null;
//回调对象的方法引用
publicController(CallBackClassobj)
但请仔细想一下,这样做的结果就使Controller类与CallBackClass对象绑定在一起,万一如果需要回调其他类型的对象,则必须修改Controller类的代码(本示例中至少得同时修改Controller类CallBackObject字段的数据类型与构造函数参数的数据类型两处代码)。
如果Controller类接收的是一个抽象的接口变量ICallBack,则任何一个实现了此接口的对象都可以被Controller类对象所回调,Controller类的代码可以保持稳定,无疑是一个好的设计方案。
事实上,接口可以看成是一个契约,它规定了某个对象必须“是什么样的”,即“接口所规定的方法,实现此接口的类一定有”。
正是有了这种确定性,Controller类才成为一个用键盘来“发出”回调的“万能控制器”。
试一试:
(1)请读者再创建另外一个回调类,让Controller类回调它。
(2)请修改Controller类,让它一次可以回调多个对象或多种类型的对象。
(1)将Controller类的CallBackObject字段改为可容纳多个ICallBack对象的容器,比如数组、ArrayList等。
(2)利用本书13.1.3节中介绍的委托,也能实现同样的回调功能。
5.接口与抽象类的区别
小结一下接口与抽象类的区别。
q抽象类是一个不完全的类,需要子类来完善它。
q接口只是对类的约束,它仅仅承诺了类能够调用的方法。
q一个类一次可以实现若干个接口,但一个类只能继承一个父类。
在实际编程中,接口的使用要比抽象类广泛得多。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第二 抽象 接口
![提示](https://static.bdocx.com/images/bang_tan.gif)