This article is currently in the process of being translated into Chinese (~94% done).
Interfaces
前面两章介绍了抽象类。界面与抽象类很象,两者都不能被实例化。不过,界面比抽象类更加概念化,因为界面中完全不存在方法实体代码。因此界面有点象一个只有抽象方法的抽象类,由于没有任何含实体代码的方法,也就不需要任何的字段。不过属性是允许有的,此外还可以有索引器和事件。可以把界面视为一个契约 - 实现某界面的类被要求实现其全部的方法和属性。不过,界面与抽象类最重要的区别在于,虽然C#禁止多继承,即子类继承多个基类,但允许一个类实现多个界面!
那么这一切用代码写出来是什么样子的呢?下面是个非常完整的例子。请先看下代码,或许自己试着运行看看,然后再继续阅读后面完整的说明:
using System;
using System.Collections.Generic;
namespace Interfaces
{
class Program
{
static void Main(string[] args)
{
List<Dog> dogs = new List<Dog>();
dogs.Add(new Dog("Fido"));
dogs.Add(new Dog("Bob"));
dogs.Add(new Dog("Adam"));
dogs.Sort();
foreach(Dog dog in dogs)
Console.WriteLine(dog.Describe());
Console.ReadKey();
}
}
interface IAnimal
{
string Describe();
string Name
{
get;
set;
}
}
class Dog : IAnimal, IComparable
{
private string name;
public Dog(string name)
{
this.Name = name;
}
public string Describe()
{
return "Hello, I'm a dog and my name is " + this.Name;
}
public int CompareTo(object obj)
{
if(obj is IAnimal)
return this.Name.CompareTo((obj as IAnimal).Name);
return 0;
}
public string Name
{
get { return name; }
set { name = value; }
}
}
}上面例子的中间部分定义了界面。该代码显示,此定义与类定义的区别仅在于所用的关键字不同 - 用interface而不是class。此外,此界面的名称前面加了个“I”字母表示界面 - 这只是编码标准,而非强制的。对界面变量的调用很灵活,但是由这些变量都被当做类变量来使用,在代码的某些部分可能会很难区别界面和类,这使得界面名称前缀的“I”字母有了意义。
然后是Describe方法的声明,之后是Name属性,同时含有get和set关键字,表明这是个可读写的属性。注意这里没有用访问修饰符(public, private, protected等),这是因为界面里不允许使用访问修饰符 - 所有成员默认都是公开(public)的。
接下来是Dog类定义。注意,这与从另一个基类继承很象,在类名称与被继承/实现的基类/界面名称之间加上冒号。不过这里的定义中一个类实现了两个界面,只需用逗号把被实现的多个界面分开即可。一个类可以实现的界面数量是没有限制的,只是这里只实现了两个 - 本地定义的界面IAnimal和.NET的IComparable界面,这是一个所有可被排序的类共享的一个界面。Dog类的代码实现了来自IAnimal界面的所有方法和属性,也实现了来自IComparablel界面的CompareTo方法。
到这里可能有人会问:既然这些全都要自己做一遍,实现所有的方法和属性,那还费那个劲定义界面干嘛?上述示例的开始部分就很好地回答了为什么值得花时间这样做的问题。代码向一个列表中加入了一批Dog(狗)实例,然后对列表进行排序。那么列表怎么知道狗的排序方法呢?因为Dog(狗)类里面有个CompareTo方法给出了对两只狗进行比较的方法。那列表又是怎么知道Dog(狗)对象有这个功能,以及要调用那个方法来比较两只狗的呢?因为代码已经告诉它了,通过实现一个承诺提供CompareTo方法的界面!这正是界面的巧妙之处。