C#编程利器之三:接口(Interface)
C#接口是一个让很多初学者容易迷糊的东西,用起来好象很简单,定义接口,然后在里面定义方法,通过继承与他的子类来完成具体的实现。但没有真正认识接口的作用的时候就觉得用接口是多此一举,当然你这样想是绝对错误的。在软件设计中有一个非常重要的原则就是:面向接口编程,依赖与接口或抽象层。可见接口在真正的开发中是多么的重要。
在之前C#编程利器之一:类(Class)一文里介绍了类的相关知识,本文主要介绍OO编程中的另一个重要知识点--接口。在某种程度上说,接口也是类,一种特殊的类或抽象类。 更准确说接口只包含方法、委托或事件的签名。方法的实现是在实现接口的类中完成的[MSDN]。
一、接口的定义
如上MSDN上对接口的定义,接口只包含方法、委托或事件的签名。这句话用更通俗点的解释便是,接口只是负责完成定义的操作,而不去实现具体的细节。如下面的IPlayer接口,它是一个玩游戏的接口,里面只是定义了相应的方法,而不带方法的具体实现,代码如下:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
6
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
7
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
8
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
9
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
10
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
11
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
12
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
13
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
14
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
15
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
16
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
17
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
以上就是一个典型的接口的定义。定义了一个名为IPlayer的接口,内部定义了两个方法GetName和Show。除了在接口里定义方法以外,我们还可以定义属性、索引及事件等,详细请查看MSDN上的定义或是相关书籍,这里以属性为例简单介绍下,在接口里只能定义不实现,具体的实现是交给其子类去完成的,那么属性应该怎么定义呢?
通常我们定义属性如下:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
6
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
7
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
8
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
9
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
那么在接口中又是怎么定义属性,并让其子类去实现呢?如下代码段:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
6
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
7
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
8
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
9
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
10
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
11
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
12
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
13
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
14
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
15
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
16
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
17
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
18
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
19
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
20
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
21
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
22
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
23
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
24
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
二、接口的实现
在本文开始部分曾经说过,接口只负责定义,不负责实现,具体的实现是交给他的子类去完成的。 OK,现在我们就以上面定义的玩游戏的接口IPlayer为例,来简单的介绍下接口的实现。
就拿我的趣味编程中的玩剪刀石头布的案例来说吧,爷爷和奶奶从小就教授小孙子各中东西,其中玩趣味游戏就他们常有的事,可小孙子还小不知道变换,每次都出剪刀,这样能赢他爷爷吗?有了这个分析,我们可以怎么做呢?上面定义了接口,我们是不是直接去实现这个接口便OK了。爷爷和小孙子玩游戏,那么就定义两个类去继承IPlayer接口。代码如下:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
6
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
7
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
8
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
9
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
游戏里只会出现这三种动作状态,所以我们可以进行封装,这里是通过类封装的,当然我们也可以通过别的相关技术来封装,比如在本系列第二篇文章《C#编程利器之二:结构与枚举(Structure and enumeration)》 里介绍的结构与枚举,本例中所出现的这三中不变的状态完全可以使用结构或枚举来封装,详细请阅读上篇文章。下面是定义爷爷(Grandpa)类和孙子(Grandson)类去实现接口(IPlayer)了。代码如下:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
6
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
7
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
8
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
9
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
10
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
11
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
12
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
13
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
14
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
15
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
16
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
17
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
18
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
19
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
20
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
21
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
22
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
3
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
6
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
7
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
8
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
9
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
10
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
11
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
12
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
13
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
14
![](https://image.cha138.com/20210611/a11cbaae81594786898653024542d899.jpg)
15
![](https://image.cha138.com/20210611/f7e2d9e047a7481cafaaa110f12cb8fb.jpg)
如上,我们的GrandPa和GrandSon就实现了接口IPlayer,如下图示:
三、接口的继承
关于这点这里就不作详细的介绍,只需要记住有这样一句话就万岁了:“一个接口可从一个或多个基接口继承”。示意性代码:
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
2
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
3
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
4
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
四、接口的特性
接口除了可以包含方法之外,还可以包含属性、索引器、事件等,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。
从类型上来说接口是引用类型的,类似于类,和抽象类的相似之处有三点:
1、不能实例化;
2、包含未实现的方法声明;
3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);
五、接口与回调
通常情况下,我们创建一个对象,并马上直接去使用它的方法。然而,在有些情况下,希望能在某个场景出现后或条件满足时才调用此对象的方法。回调就可以解决这个“延迟调用对象方法”的问题。这个被调用方法的对象称为回调对象。
首先创建一个回调对象,然后再创建一个控制器对象,将回调对象需要被调用的方法告诉控制器对象.控制器对象负责检查某个场景是否出现或某个条件是否满足.当此场景出现或此条件满足时,自动调用回调对象的方法.示意性代码如下:
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
2
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
3
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
4
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
5
![](https://image.cha138.com/20210611/60f0bc4f68074adabd2149e8f16ce9be.jpg)
6
![](https://image.cha138.com/20210611/cea484be0bc94967a26ad2bad476cffc.jpg)
7
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
8
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
9
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
10
![](https://image.cha138.com/20210611/29c105ea4f444f86a864a9162a6b16b5.jpg)
11
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
12
![](https://image.cha138.com/20210611/01509ed56bec49b3a60aa37cb93649d8.jpg)
以上是关于C#编程利器之三:接口(Interface)的主要内容,如果未能解决你的问题,请参考以下文章
c# 并发编程系列之三:使用 Parallel 开始第1个多线程编码