一,接口概述
接口为类提供了又一种实现多态性的方法(继承是第一种),与抽象类类似的是接口定义了一系列成员,调用者可依赖这些成员来支持一些特定的功能, 但与抽象类不同,接口不包含任何实现。接口可以看作是一种 "契约"。如果一个类继承了一个接口,那么表示这个类承诺它将提供这个接口定义的某种功能,但这个功能的具体实现则是由这个类完成的。接口只表示这个类 "会去做什么",但"怎样做" 则是由类自己实现的,和接口没有任何的关系。
接口的一些关键特征:
- 接口不包含任何数据和任何实现,接口只声明方法。
- 接口的宗旨是定义多个类共同遵守的契约,所以接口的成员都自动定义为public的。
- 一个类只能有一个基类,但可以实现多个接口。
- 如果接口包含特定的数据,他会使用属性而不是字段。
- 接口永远不能被实例化,不能使用new 来创建一个接口,它也不能有构造器和终结器
- 接口不能包含static成员
- 接口成员不能显示的使用 abstract 修饰符
二,接口的实现 (显式实现 和 隐式实现)
关于接口的实现,还是先看一段代码吧。
using System;
using System.Collections.Generic;
using System.Text;
namespace interfaceTest1
{
interface ISampleExplicitInterface
{
void ExplicitSampleMethod();
}
interface ISampleImplicitInterface
{
void ImplicitSampleMethod();
}
class ImplementationClass : ISampleExplicitInterface, ISampleImplicitInterface
{
// Explicit interface member implementation:
void ISampleExplicitInterface.ExplicitSampleMethod()
{
Console.WriteLine("ImplementationClass implement the ExplicitSampleMethod");
}
public void ImplicitSampleMethod()
{
Console.WriteLine("ImplementationClass implement the ImplicitSampleMethod");
}
}
public class Progarm
{
static void Main()
{
// Declare an interface instance.
ISampleExplicitInterface obji = new ImplementationClass();
ImplementationClass objci = new ImplementationClass();
ImplementationClass objc = new ImplementationClass();
// Call the member.
obji.ExplicitSampleMethod();
((ISampleExplicitInterface)objci).ExplicitSampleMethod();
objc.ImplicitSampleMethod();
Console.ReadKey();
}
}
}
可以注意到这段程序中定义了两个接口类,红色和蓝色表示的,分别是 显式和 隐式的接口。
声明显式接口成员:在类中声明一个显式的接口成员时需要在成员名前附加接口名前缀。
声明隐式接口成员:在类中声明一个隐式的接口成员时不需要在成员名前附加接口名前缀。
显式和隐式的区别:
显式和隐式的接口成员最大的区别不是在于声明的方式,而是在于从类外部的可访问性。
可以看到隐式接口类似于类自身的一个成员,但显式的则不是。
隐式接口可以从类中直接访问,但显式接口则必须把类转换为接口才能调用。
显式和隐式接口的设计原则:
有些基本原则可以帮助我们选择什么时候使用显式方式,什么时候使用隐式方式。
1) 成员是不是类的核心功能。
例如:实现一个手机类,打电话是手机的核心功能,所以 make_call() 这个接口则可以设计成隐式的,所有的手机都会有的接口。但玩游戏则不一定是手机的核心功能,所以 play_game() 则可以作为显式的接口,为特定的手机对象来使用。
2) 接口成员名作为类成员是否恰当。
假设有一个 ITrace接口,接口中有一个Dump() 的成员,功能是把数据写入日志文件中。假设在一个 Person 类 或 Car 类中隐式的实现 Dump() 接口,从外部看 Dump() 则变成了 Person 或 Car的一个成员,这很容易让人产生混淆。Person 或 Car要Dump() 什么,或为什么Dump() 的问题就出来了。
3) 是否已经有一个同名的类成员。
显式接口会唯一性的区别一个成员,所以类中可以存在一个与显式接口成员同名的成员。
三,接口的继承
接口的继承与类的继承无论是语法上还是意义上都很类似,实现代码复用和理清逻辑关系的一种方式。
它还可以实现多接口的继承。
实例代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace InterfaceTest2inherit
{
interface IBase1
{
void FuncBase1();
}
interface IBase2
{
void FuncBase2();
}
interface IInherit1 : IBase1, IBase2
{
void FuncInherit1();
}
class Tester1 : IInherit1
{
// 这里显式的实现了IBase1的接口FuncBase1,虽然 IInherit1 继承自IBase1,
// 但IInherit1不能显式的实现 FuncBase1的基接口
void IBase1.FuncBase1()
{
Console.WriteLine("Executing Tester1 Interface FuncBase1");
}
// 编译错误
//void IInherit1.FuncBase1()
//{
// Console.WriteLine("Executing Tester1 Interface FuncBase1");
//}
public void FuncBase2()
{
Console.WriteLine("Executing Tester1 Interface FuncBase2");
}
public void FuncInherit1()
{
Console.WriteLine("Executing Tester1 Interface FuncInherit1");
}
}
class Tester2 : IInherit1
{
void IBase1.FuncBase1()
{
Console.WriteLine("Executing Tester1 Interface FuncBase1");
}
public void FuncBase2()
{
Console.WriteLine("Executing Tester1 Interface FuncBase2");
}
public void FuncInherit1()
{
Console.WriteLine("Executing Tester1 Interface FuncInherit1");
}
}
class Program
{
static void Main(string[] args)
{
Tester1 objTest1 = new Tester1();
((IBase1)objTest1).FuncBase1();
objTest1.FuncBase2();
objTest1.FuncInherit1();
Console.ReadKey();
}
}
}
代码中演示了接口的使用方法,接口的继承和实现。
代码看起来还是比较直观的。
四,接口与抽象类的比较
接口是C#中的一种数据类型,它不能实例化,要访问接口的实例只能通过对实现了接口的对象来进行。
接口更近似于抽象类。下表列举了抽象类和接口间的区别,可以在使用时参考一下,根据实际情况选择恰当的方式。