其实称本篇为多态还是有些牵强,因为在类的继承中也是存在多态的,例如我们的重写机制,但可以设想这样一个场景:飞行这个动作,鸟可以飞行,飞机可以飞行,而飞机其实和鸟没有父子关系的,他们共同拥有的是行为:飞行。所以本篇博客着重介绍这一点:如何通过接口来处理行为一致(横向关系)而非一脉相承(纵向关系)的关系。本篇的结构如下:
为什么要有接口?在介绍了类和抽象类(我的感觉就是抽象类属于类和接口的中间产物,既有继承基类的能力,也有扩展抽象成员方法的能力)之后,我们知道,可以通过重写和实现抽象成员来扩展和组织自己的内容。那么接口有啥必要呢?,既然有类,那还要接口做什么呢?:
接口的定义如下,
interface IFileCompression
{
void Compress(string targetFileName, string[] fileList);
void Uncompress(string compressedFileName, string expandDirectoryName);
}
需要注意以下几点:
抽象类如何通过接口甩锅呢?通过如下方式抽象类可以不实现接口方法而是让派生类实现。
public interface Ifoo
{
void Bar();
}
public abstract class Foo : Ifoo
{
public abstract void Bar();
}
public class Child : Foo
{
public override void Bar()
{
throw new NotImplementedException();
}
}
如何使用接口实现多态,其实和隐式转型类似哦。
1,定义接口和抽象类
public interface IListable
{
// Return the value of each column in the row
string[] ColumnValues
{
get;
}
}
public abstract class PdaItem
{
public PdaItem(string name)
{
Name = name;
}
public virtual string Name {
get; set; }
}
2,定义两种不同的实现类
public 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 " };
}
}
// ...
}
public 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" };
}
}
// ...
}
3,定义接口调用方的类和调用方法
public 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);
}
}
}
4,定义入口,方法实现
public static void Main()
{
Contact[] contacts = new Contact[]
{
new Contact(
"Dick", "Traci",
"123 Main St., Spokane, WA 99037",
"123-123-1234"),
new Contact(
"Andrew", "Littman",
"1417 Palmary St., Dallas, TX 55555",
"555-123-4567"),
new Contact(
"Mary", "Hartfelt",
"1520 Thunder Way, Elizabethton, PA 44444",
"444-123-4567"),
new Contact(
"John", "Lindherst",
"1 Aerial Way Dr., Monteray, NH 88888",
"222-987-6543"),
new Contact(
"Pat", "Wilson",
"565 Irving Dr., Parksdale, FL 22222",
"123-456-7890"),
new Contact(
"Jane", "Doe",
"123 Main St., Aurora, IL 66666",
"333-345-6789")
};
// Classes are cast implicitly to
// their supported interfaces
ConsoleListControl.List(Contact.Headers, contacts);
Console.WriteLine();
Publication[] publications = new Publication[3] {
new Publication(
"The End of Poverty: Economic Possibilities for Our Time",
"Jeffrey Sachs", 2006),
new Publication("Orthodoxy",
"G.K. Chesterton", 1908),
new Publication(
"The Hitchhiker's Guide to the Galaxy",
"Douglas Adams", 1979)
};
ConsoleListControl.List(
Publication.Headers, publications);
}
隐式调用接口方法,会使用各个派生类对接口各自的实现方式:
Contact会实现如下内容:输出以下四个属性:FirstName、LastName、Phone、Address
Publication会实现如下内容:输出:Title、Author、Year
分为显式和隐式、接口继承,以及多接口继承。
接口其实分为两种实现,显式和隐式,上面介绍的实现是隐式的,感觉像是我们大多数时候使用的场景,但有些时候也会用到显式实现:
public class Program
{
public static void Main()
{
string[] values;
Contact contact1, contact2 = null;
// ...
// ERROR: Unable to call ColumnValues() directly
// on a contact
// values = contact1.ColumnValues;
// First cast to IListable
values = ((IListable)contact2).ColumnValues;// 显式
var result = contact1.CompareTo(contact2);// 隐式
}
}
public class Contact : PdaItem, IListable, IComparable
{
// ...
public Contact(string name)
: base(name)
{
}
#region IComparable Members
public int CompareTo(object obj) //隐式实现
{
//...
}
#endregion
#region IListable Members
string[] IListable.ColumnValues //显式实现
{
get
{
return new string[]
{
FirstName,
LastName,
Phone,
Address
};
}
}
#endregion
protected string LastName {
get; set; }
protected string FirstName {
get; set; }
protected string Phone {
get; set; }
protected string Address {
get; set; }
}
显式实现: values = ((IListable)contact2).ColumnValues;
,显式实现方式只能通过接口本身调用!不能使用virtual、override或者public来修饰!
隐式实现: var result = contact1.CompareTo(contact2);
隐式实现方式允许类自身调用!virtual、override或者public都是可选参数。
对于隐式和显式实现的接口成员,关键区别不在于成员声明的语法,而在于通过类型的实例而不是接口访问成员的能力。 建立类层次结构时需要建模真实世界的“属于”(is a)关系——例如,长颈鹿“属于”哺乳动物。这些是“语义”(semantic)关系。而接口用于建模“机制”(mechanism)关系。PdaItem“不属于”一种“可比较”(comparable)的东西,但它仍可实现IComparable接口。该接口和语义模型无关,只是实现机制的细节。显式接口实现的目的就是将“机制问题”和“模型问题”分开。要求调用者先将对象转换为接口(比如IComparable),然后才能认为对象“可比较”,从而显式区分你想在什么时候和模型沟通,以及想在什么时候处理实现机制。
一般来说,最好的做法是将一个类的公共层面限制成“全模型”,尽量少地涉及无关的机制
大多数情况我们使用隐式的方式。
接口可以多继承,但是需要注意的是,如果显式的实现接口,则必须使用最初声明该接口的名称。
namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter08.Listing08_08
{
interface IReadableSettingsProvider
{
string GetSetting(string name, string defaultValue);
}
interface ISettingsProvider : IReadableSettingsProvider
{
void SetSetting(string name, string value);
}
这样调用Getting方法是不对的:
class FileSettingsProvider : ISettingsProvider,
IReadableSettingsProvider
{
#region ISettingsProvider Members
public void SetSetting(string name, string value)
{
// ...
}
#endregion
#region IReadableSettingsProvider Members
string ISettingsProvider.GetSetting(string name, string defaultValue)
{
return name + defaultValue; //just returning this for the example
}
#endregion
}
}
应该这样调用:
string IReadableSettingsProvider.GetSetting(string name, string defaultValue)
{
return name + defaultValue; //just returning this for the example
}
接口和类一样,也可以继承多个接口:
namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter08.Listing08_09
{
interface IReadableSettingsProvider
{
string GetSetting(string name, string defaultValue);
}
interface IWriteableSettingsProvider
{
void SetSetting(string name, string value);
}
interface ISettingsProvider : IReadableSettingsProvider,
IWriteableSettingsProvider
{
}
}
接口也可以被当做扩展方法使用,而且更加灵活,比类更加实用,而且该扩展方法不仅可以是特定类型,还允许特定类型的集合。
static class Listable
{
public static void List(
this IListable[] items, string[] headers)
{
int[] columnWidths = DisplayHeaders(headers);
for(int itemCount = 0; itemCount < items.Length; itemCount++)
{
if (items[itemCount] != null)
{
string[] values = items[itemCount].ColumnValues;
DisplayItemRow(columnWidths, values);
}
}
}
}
其实二者各有千秋吧,一种是模型角度,一种是机制角度,一种纵向,一种横向。