C#本质论笔记 第7章 接口

接口概述

一样,在接口中可以定义一个和多个方法、属性 、索引指示器和事件。但与不同的是,接口中仅仅是它们的声明,并不提供实现。因此接口是函数成员声明的集合。如果类或结构从一个接口派生,则这个类或结构负责实现该接口中所声明的所有成员。一个接口可以从多个接口继承,而一个类或结构可以实现多个接口。由于 C# 语言不支持多继承,因此,如果某个类需要继承多个类的行为时,只能使用多个接口加以说明。

对接口的理解:

  • 接口只有声明,没有实现,由继承该接口的类或者结构实现。
  • C#的类只支持单继承,采用接口变相实现了多继承。
  • 接口更多的是一种规范约束,要求继承该接口的类或者结构必须对接口成员进行实现。

接口声明

接口声明是一种类型声明,它定义了一种新的接口类型。接口声明格式如下:

属性 接口修饰符 interface 接口名:基接口{接口体}

其中,关键字interface、接口名和接口体时必须的,其它项是可选的。接口修饰符可以是new、public、protected、nternal和private。例子:

1
2
3
4
5
6
7
public interface IExample {
//所有接口成员都不能包括实现
string this [int index] { get; set; } //索引指示器声明
event EventHandler E; //事件声明
void F (int value); //方法声明
string P { get; set; } //属性声明
}

声明接口时,需注意以下内容:

  • 接口成员只能是方法、属性、索引指示器和事件,不能是常量、域、操作符、构造函数或析构函数,不能包含任何静态成员。
  • 接口成员声明不能包含任何修饰符,接口成员默认访问方式是public

接口的继承

类似于类的继承性,接口也有继承性。派生接口继承了基接口中的函数成员说明。接口允许多继承,一个派生接口可以没有基接口,也可以有多个基接口。在接口声明的冒号后列出被继承的接口名字,多个接口名之间用分号分割。例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

interface IControl {
void Paint ();
}
interface ITextBox : IControl //继承了接口Icontrol的方法aint()
{
void SetText (string text);
}
interface IListBox : IControl //继承了接口Icontrol的方法Paint()
{
void SetItems (string[] items);
}
interface IComboBox : ITextBox, IListBox { //可以声明新方法
}

上面的例子中,接口ITextBoxIListBox都从接口IControl中继承,也就继承了接口 IControlPaint方法。接口IComboBox从接口ITextBoxIListBox中继承,因此它应该继承了接口ITextBoxSetText方法和IListBoxSetItems方法,还有IControlPaint方法。

类对接口的实现

前面已经说过,接口定义不包括函数成员的实现部分。继承该接口的类或结构应实现这些函数成员。这里主要讲述通过类来实现接口。类实现接口的本质是,用接口规定类应实现那些函数成员。用类来实现接口时,接口的名称必须包含在类声明中的基类列表中。

加入定义一个描述个人情况的类Person,从类Person可以派生出其它类,例如:工人类、公务员类、医生类等。这些类有一些共有的方法和属性,例如工资属性。一般希望所有派生类访问工资属性时用同样变量名。该属性定义在类Person中不合适,因为有些人无工资,如小孩。如定义一个类作为基类,包含工资属性,但C#不支持多继承。可行的办法是使用接口,在接口中声明工资属性。工人类、公务员类、医生类等都必须实现该接口,也就保证了它们访问工资属性时用同样变量名。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;

public interface I_Salary //接口
{
decimal Salary //属性声明
{ get; set; }
}
public class Person {
private string P_name = "张三"; //P_name是私有字段
private int P_age = 12; //P_age是私有字段
public void Display () //类的方法声明,显示姓名和年龄
{
Console.WriteLine ("姓名:{0},年龄:{1}", P_name, P_age);
}
public string Name //定义属性Name
{
get { return P_name; }
set { P_name = value; }
}
public int Age //定义属性Age
{
get { return P_age; }
set { P_age = value; }
}

}

public class Employee : Person, I_Salary //Person类是基类,I_Salary是接口
{ //不同程序员完成工人类、医生类等,定义工资变量名称可能不同
private decimal salary;
public new void Display () {
base.Display ();
Console.WriteLine ("薪金:{0} ", salary);
}
//工人类、医生类等都要实现属性Salary,保证使用的工资属性同名
public decimal Salary {
get { return salary; }
set { salary = value; }
}
}
public class Test {
public static void Main () {
Employee S = new Employee ();
S.Name = "田七"; //修改属性Name
S.Age = 20; //修改属性Age
S.Salary = 2000; //修改属性Salary
S.Display ();
}
}
/* 输出
姓名:田七,年龄:20
薪金:2000
*/

抽象类和接口比较

抽象类 接口
相同点
可以被继承 可以被继承
可以有方法的声明 可以有方法的声明
不可以被实例化 不可以被实例化
子类必须实现基类的方法除非子类是抽象类 子类必须实现基类的方法除非子类是抽象类
以下为不同点
一个类只能直接继承一个类(包括抽象类) 一个类可以直接继承多个接口
是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性; 是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
是一个不完整的类,需要进一步细化 是一个行为规范。
抽象基类可以定义字段、属性、方法实现。 接口只能定义属性、索引器、事件、和方法声明,不能包含字段。
更多的是定义在一系列紧密相关的类间 大多数是关系疏松但都实现某一功能的类中
只能作用于引用类型 可以作用于值类型和引用类型
抽象类既可以定义规则,还可能提供已实现的成员。 只能定义抽象规则

结尾