C#接口和抽象类
从类继承是很强大的机制,但继承真正强大之处是能从接口继承。
理解接口
假定要定义一个新类来存储对象集合,并且提供方法允许应用程序根据集合中的对象类型顺序获取对象;比如字符串类型,就根据字符串的的排序规则排序;比如int类型,就根据大小排序。
但是因为容纳的对象类型不同,所以定义排序方法时不知道怎么对对象进行排序。现在的问题是,如何提供一个方法,对定义集合类时不知道类型的对象进行排序 。从表面看,貌似可以通过声明一个能由派生类重写的虚方法来解决。但是集合类与对象之间不存在任何的继承关系。
合理方法是规定集合中所有对象都必须提供一个可由集合的排序方法调用的方法,以实现对象的相互比较,即定义一个接口,在接口中包含被调用的方法(实现了接口的类必然包含接口规定的所有方法),在接口中定义一个排序方法,只描述排序功能,具体实现交给实现它的类。
定义接口
interface Icomparable
{
int CompareTo(object obj);
}
和类的定义相似,只是使用interface而不是class关键字,在接口中按照与类和结构一样的方式声明方法。不允许指定任何访问修饰符(public,private,protected都不行)
另外,接口中的方法是没有实现的,它们只是声明,实现接口的所有类型都必须提供自己的实现
接口不包含任何数据;不可以向接口添加字段(私有的也不行)
实现接口
interface ILandBound
{
int NumberOfLegs();
}
class Horse : ILandBound
{
...
public int NumberOfLegs()
{
return 4;
}
}
上述例子是为所有动物提供了名为NumberOfLegs(腿数)的方法,可定义一个接口ILandBound来包含该方法,在Horse类来实现。因为不同的动物腿数不同,所以返回值也不同,所以定义一个接口包含此方法。
实现接口时必须保证每个方法都完全匹配对应的接口方法
- 方法名和返回类型完全匹配。
- 所有参数(包括ref和out关键字修饰符)都完全匹配
- 用于实现接口的所有方法都必须具有public可访问性。
一个类可以从一个类继承的同时实现接口
class Mammal//哺乳类
{
...
}
class Horse : Mammal,ILandBound
{
...
}
先写基类名,在写逗号,在写接口名
一个接口可从另一个接口继承,但这叫接口扩展,而不是继承
通过接口引用类
接口变量也能引用实现了该接口的类的对象
Horse myHorse = new Horse();
IlandBound iMyHourse=myHorse;//合法
但是不能将ILandBound对象赋给Horse变量
使用多个接口
一个类最多只能有一个基类,但可以实现数量不限的接口,但必须实现接口规定的所有方法
显式实现接口
如果不同的接口方法名相同,那么就要在类中显式实现接口
interface IlandBound
{
int NumberOfLegs();//腿数
}
interface IJourney
{
int NumberOfLegs();//跑的站数
}
class Horse : ILandBound,IJourney
{
int ILandBound.NumberOfLegs()
{
return 4;//马有四条腿
}
int IJourney.NumberOfLegs()
{
return 3;//跑了三站
}
}
区分哪个方法实现的是哪个接口
接口的限制
牢记接口永远不包含任何实现
- 不能再接口定义任何字段,包括静态字段。字段本质上是类或结构的实现细节
- 不能在接口定义任何构造器。构造器也是上述两个的实现细节
- 不能在接口为任何方法指定访问修饰符。接口所有方法都隐式为公共方法
- 不能在接口中嵌套任何类型(枚举、结构、类或其他接口)
- 不允许继承结构或类。结构和类含有实现;如果从他们继承,就会继承实现
抽象类
如果羊和马两个类都要继承哺乳类和陆地类,并且实现食草方法的接口,那么他们代码就会有很多重复,比如吃草的方法相同等…
所以要在定义一个新类食草哺乳类,继承哺乳类的同时并实现食草接口,然后马类和羊类只要继承食草哺乳类和陆地类即可,不用两个类都去实现食草方法
但是,食草哺乳类唯一的作用是让马和羊等类从它继承,而不希望它是一个单独存在的实体,也就是不允许创建这个类的实例
所以必须将这个类显示声明为抽象类,用abstract关键字实现
abstract class GrazingMammal : Mammal,IGrazable
{
...
}
class Horse : GrazingMammal,ILandBound
{
...
}
class Sheep : GrazingMammal,ILandBound
{
...
}
抽象方法
抽象类可包含抽象方法。原则上与虚方法相似。只是不含方法主体,派生类必须重写这种方法,抽象方法不能私有。
如一个方法在抽象类中提供默认实现没有意义,但又需要派生类提供该方法的实现,就适合定义成抽象方法。
密封类
如果不想一个类作为基类使用,就用sealed关键字防止作为基类
sealed class Horse
{
...
}
密封类不能声明任何虚方法,而且抽象类不能密封