C#8.0本质论第八章–接口
多态性还可以通过接口实现。
接口不能包含任何实现,但是这一限制从C#8.0开始被取消了。
接口实现关系是一种“能做”关系,类型能做接口锁规定的事情。接口订立了契约。
8.1接口概述
接口采用PascalCase规范并附加“I”前缀。
在C#8.0之前接口不能包含实现和数据。
接口声明的成员描述了在实现该接口的类型中必须能访问的成员。所有,C#不允许为接口成员使用访问修饰符,所有成员都自动公共。
8.2通过接口实现多态性
8.3接口实现
声明类来实现接口类似于从基类派生(基类在前,接口顺序任意),类可实现多个接口,但只能从一个基类直接派生。
抽象类既可以将接口方法映射为抽象方法(加abstract修饰符),也可以将他实现为非抽象方法。
在类型中实现接口成员时有两种方式:显式和隐式
8.3.1显式成员实现
显式实现的方法只能通过接口本身调用,最典型的做法就是将对象转型为接口。
在接口成员名称全附加接口名称前缀来显式实现接口成员。
public class Contact : PdaItem, IListable
{
//显式实现
string?[] IListable.CellValues
{
get
{
return new string?[]
{
FirstName,
LastName,
Phone,
Address
};
}
}
}
由于显式接口实现直接和接口关联,所以没必要使用virtual、override或者public修饰它们,这些修饰符是不被允许的。
8.3.2隐式成员实现
不加前缀就是隐式实现的,只要求成员是公共的,且签名相符,不要求执行转型,因为成员可直接调用。
隐式成员实现必须是public的,而virtual是可选的,取决于是否允许派生类重写实现。去掉virtual会导致成员被密封。
8.3.3显式与隐式接口实现的比较
对于隐式和显式实现的接口成员,关键区别不在于成员声明的语法,而在于通过类型的实例访问成员的能力。
显式接口实现的目的就是将“机制问题”和“模型问题”分开。
8.4在实现类和接口之间转换
实现类可隐式转换为接口,无须转型操作符。无法保证从接口像实现类型的向下转型能成功。
8.5接口继承
显式实现接口成员时,必须在完全限定的接口成员名称中引用最初声明它的接口的名称。
“继承”这个词用得没错,但更准确的说法是接口代表契约,一份契约可以指定另一份契约也必须遵守的条款。
8.6多接口继承
8.7接口上的扩展方法
扩展方法还能作用于接口。
public 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].CellValues;
DisplayItemRow(columnWidths, values);
}
}
}
}
C#还可以位该类型的对象集合添加扩展方法。
8.8版本升级
C#8.0开始,”接口不可变“这一原则有了少许变化。C#8.0允许在接口中为方法提供默认实现。
8.8.1C#8.0之前的接口版本升级
8.8.2C#8.0之后的接口版本升级
8.8.3基于保护访问成员实现额外的封装和多态
8.9扩展方法与默认接口成员
8.10比较接口和抽象类
C#8.0接口几乎成为抽象类的功能超集。
8.11比较接口和特性
有时用无任何成员的接口来描述关于类型的信息。例如有人会用IObsolete的标记接口(marker interface)来指出某类型已被另一类型取代,一般认为这是对接口机制的滥用。接口应表示某类型能执行的功能,而非陈述关于类型的事实。这时改为使用特性(attribute)。