先描述接口的概念
C#并非只能通过继承使用多态性,也可以通过接口使用。和抽象类不同,接口不包含任何实现(C++并不会特意去区分抽象类和接口,实际上C++的接口就是通过抽象类实现,其并没有interface和abstract关键字)。
接口的意义在于能够将实现细节和服务完全隔离开。举个例子:我们都知道插座可以供电,只要我们的插头能够插入插座就可以获得电力,也就是说,我们并不关心插座当中的电力是来自于核电、煤电还是太阳能发电,我们只关心我们的插头是否能插入插座进而充电。在这里插座就是接口,插座当中的电力的形式就是实现细节(核电、煤电、太阳能发电等),而插座的供电能力就是服务。因此这里的“将实现细节和服务完全隔离开”指的就是:无论插座(接口)当中的电力来源(实现细节)是什么形式,都不会影响插座(接口)的供电能力(服务)。这就是所谓“将实现细节和服务完全隔离开”。
那么在编程的时候,假如涉及到多态性,如何在继承和接口之间取舍呢?这里有个核心思想可以决定:继承既可以共享成员签名,又可以共享实现,当满足这两个条件的时候应当使用继承;接口只能共享成员签名,而不共享实现,当只满足这种情况的时候应当考虑使用接口。举个例子:都知道压缩有各种各样的格式:.zip、.cab、.tar、.tar.gz、.rar、.gz等。而每个压缩格式的实现方式都不同,然而它们都属于压缩的范畴,假如为每个压缩格式单独创建一个类,又无法提供标准的调用规范。这里就要考虑是使用继承还是接口。先假设使用继承,因为各种压缩格式实现方式不同,因此基类当中并不存在通用的代码供各种压缩格式派生类调用,也就是并没有共享实现,这会使得基类毫无意义(继承核心思想:共享成员标签(满足),共享实现(不满足))。因此应当使用接口(接口核心思想:共享成员标签(满足),不共享实现(满足))。压缩接口代码如下:
interface IFileCompression
{
void Compress(string targetFileName,string[] fileList);
void Uncompress(string compressedFileName, string expandDirectoryName);
}
只要各种压缩格式实现了这个接口以及接口中的方法,那么在需要压缩的时候,只需执行到IFileCompressoin接口的一次转型,然后调用它的成员方法即可,根本不用关心具体是什么类在实现哪些方法(接口是一致的),这正是多态性的一个反映。(后面以较为完整的代码诠释接口实现多态性)
要注意几点:1、接口既不包含实现,也不包含数据。2、C#不允许为借口成员使用访问修饰符,所有成员自动定义为public成员。
再描述接口实现多态性
//接口
interface IListable
{
//接口可以提供属性,但是不能实现属性
string[] ColumnValues
{
get;
}
}
//抽象类
public abstract class PdaItem
{
public PdaItem(string name)
{
Name = name;
}
public virtual string Name{get;set;}
}
//C#只能是单继承,实现接口的类另当别论
class Contact: PdaItem,IListable
{
public Contact(string firstName, string lastName,
string address, string phone):base(null)
{
FirstName = firstName;
LastName = lastName;
Address = address;
Phone = phone;
}
public string FirstName {get;set;}
public string LastName {get;set;}
public string Address {get;set;}
public string Phone {get;set;}
//实现了接口的方法
public string[] ColumnValues
{
get
{
return new string[]
{
FirstName,
LastName,
Phone,
Address
}
}
}
public static string[] Headers
{
get
{
return new string[]
{
"First Name","Last Name ",
"Phone ",
"Address"
}
}
}
}
class Publication : IListable
{
public Publication(string title,string author,int year)
{
Title = title;
Author = author;
Year = year;
}
public string Title {get;set;}
public string Author{get;set;}
public int Year {get;set}
//实现接口的方法
public string[] ColumnValues
{
get
{
return new string[]
{
Title,
Author,
Year.ToString()
}
}
}
public static string[] Headers
{
get
{
return new string[]
{
"Title ",
"Author ",
"Year"
}
}
}
}
class Program
{
//类Contact和Publication毫不相关,然而由于其实现了接口的方法的缘故,在传递参数赋值给IListable的时候,调用的方法的实现细节是相应具体类型的方法实现
public static void Main()
{
Contact[] contacts = new Contact[3];
contacts[0] = new Contact(
"Dick", "Traci",
"123 Main St., Spokane, WA 99037",
"123-123-1234"
);
contacts[1] = new Contact(
"Andrew","Littman",
"1417 Palmary St., Dallas, TX 55555",
"555-123-4567"
);
contacts[2] = new Contact(
"Mary", "Hartfelt",
"1520 Thunder Way, Elizabethton, PA 44444",
"444-123-4567"
);
ConsoleListControl.List(Contact.Headers,contacts);
Console.WriteLine();
Publication[] publications = new Publication[3]{
new Publication(
"Celebration of Discipline",
"Richard Foster", 1987
),
new Publication(
"Orthodoxy",
"G.K. Chesteron",1908
),
new Publication(
"The Hitchhiker's Guide to the Galaxy",
"Douglas Adams",1979
)
};
ConsoleListControl.List(Publication.Headers,publications);
}
}
class ConsoleListControl
{
public static void List(string[] headers,IListable[] items)
{
int[] columnWidths = DisplayHeaders(headers);
for(int count = 0; count < items.Length; count++)
{
string[] values = items[count].ColumnValues;
DisplayItemRow(columnWidths,values);
}
}
private static int[] DisplayHeaders(string[] headers)
{
}
private static vod DisplayItemRow(int[] columnWidths,string[] values)
{
}
}
要实现的接口和基类名称以逗号分隔(基类在前,接口顺序任意),类可以实现多个接口但是只能从一个基类直接派生。接口不能实例化,也不能使用new创建接口,因此接口没有构造器和终结器。