这里关于接口的定义和为什么使用接口不做多说,大家也都知道。
下面主要说一下关于接口的使用过程中关于多态性问题,这个是我比较关注的问题。
- 接口的限制
不允许在接口中包含任何字段,即使是static字段。
不允许在接口中包含任何构造函数。
不允许在接口中包含任何析构函数。
不允许为接口中的任何方法指定访问修饰符。
不允许在接口中嵌套任何类型。
不允许从结构或者类中继承一个接口,虽然一个接口能从另一个接口继承。
下面给出一个我们即将用到的简单的接口的定义:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication4 { interface IWork { void test(); int count(int index); } }
注意一个良好的编程习惯: 定义的接口以 I 开头,这样可以清楚的区分接口和其他类。
接口的实现
接口的实现可以分为两种,显示实现接口和非显示的实现接口。
显示实现接口
使用接口名做为方法名的前缀,成为显示接口实现,也是我们应该养成的一个编程习惯。(但是我自己的练习中发现显示实现的接口无法实现多态性,可能规定的就不能实现多态性,这点接下来会讲)
用于实现一个接口的所有的方法都必须有public 的访问型,但是,如果你是显示的实现接口,则不可以为显示实现的接口指定访问修饰符。
使用显示实现的接口不能声明为 virtual ,但在省略接口名以后则可以声明为virtual.
因为显示实现的接口不能声明为 virtual ,所以显示实现的接口不能实现多态性。但是显示接口的实现有很多优势,可以写出更清晰更容易维护的代码,增强可预测性。如果一个类实现的多个接口中有相同的函数的声明,这样我们只有通过显示的实现接口来避免歧义。
需要注意的一点:显示实现接口以后,这个显示的实现的接口默认具有 public 可访问性,对实现的接口的方法的访问,只能通过接口名来访问,不能通过实现接口类的实例对象来访问,实例对象是找不到显示实现的接口的方法的。只有通过一个接口类型的变量引用实现接口的类的对象,并通过这个接口引用访问类中实现的接口方法。
显示实现的接口:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication4 { class MyWork:IWork { public MyWork() { } void IWork.test() { Console.WriteLine("interface test"); //throw new NotImplementedException(); } int IWork.count(int index) { Console.WriteLine("interface count"); //throw new NotImplementedException(); return 0; } } class SonOfMyWork : MyWork { public SonOfMyWork() : base() { Console.WriteLine("class : SonOfMyWork"); } public virtual void test() { Console.WriteLine("SonOfMyWork, interface explicitly completed."); } } }
在 MyWork 这个类中,接口都是显示实现的,在SonOfMyWork中,继承了MyWork, 并且重新声明了 test() 函数。下面看一下运行结果:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { MyWork myWork = new MyWork(); IWork work = myWork; work.test(); work.count(5); Console.WriteLine(""); SonOfMyWork son = new SonOfMyWork(); son.test(); Console.WriteLine(""); work = son; work.test(); //Console.WriteLine("/"); //MyWork2 myWork2 = new MyWork2(); //myWork2.test(); //Console.WriteLine("/"); //SonOfMyWork2 son2 = new SonOfMyWork2(); //son2.test(); //work = son2; //work.test(); } } }
work.test(); 这里通过接口调用方法,输出的结果就是接口的显示实现。son.test(); 因为son 对象是继承于MyWork , SonOfMyWork 定义的test() 方法已经隐藏了父类的方法,所以调用的是在SonOfMyWork 类中定义的test() 方法。 这里也说明了接口的显示实现不能实现多态性,但是以后在有类继承SonOfMyWork 类的时候,就可以再次将test() 实现多态性。
work = son; work.test(); 这里通过接口访问实现接口类的派生类,用来测试接口的多态性,注意,这里的输出是接口的显示实现中输出的语句,并没有访问到这个派生类中的test() 方法,而是访问的接口的显示实现的类中的方法。
以下是上面程序的输出:
interface test
interface count
class : SonOfMyWork
SonOfMyWork, interface explicitly completed.
interface test
请按任意键继续. . .
上面的程序可以看到接口的显示实现不能实现多态性,但是接口的显示实现确实有很多好处,应该注意接口的显示实现。
接口的隐式实现
下面的类是关于接口的隐式实现:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication4 { class MyWork2:IWork { public virtual void test() { Console.WriteLine("Interface implicitly implement."); //throw new NotImplementedException(); } public virtual int count(int index) { //throw new NotImplementedException(); return 0; } } class SonOfMyWork2 : MyWork2 { public override void test() { //base.test(); Console.WriteLine("Interface in SonOfMyWork2"); } } }
接口的隐式实现就可以声明为public, virtual 的,并且可以实现多态性。
关于隐式实现的测试代码:
Console.WriteLine("/"); MyWork2 myWork2 = new MyWork2(); myWork2.test(); Console.WriteLine("/"); SonOfMyWork2 son2 = new SonOfMyWork2(); son2.test(); work = son2; work.test();
work = son2;work = son2; 通过接口引用实现接口的类的派生类的对象。输出结果:
/
Interface implicitly implement.
/
Interface in SonOfMyWork2
Interface in SonOfMyWork2
请按任意键继续. . .
这里可以看到隐式实现的接口中,通过对象访问test() 方法和通过接口对派生类对象的引用调用test() 方法都有一致的输出,可以证明隐式实现的接口可以实现多态性。
特别说明:对于这个网页排版工具我不是很会用,所以文章写的有点排版乱,以后尽量的用熟这个工具,避免大家看的不舒服。