C#中的虚方法(virtual method
)、接口(interface
)、抽象类(abstract class
)和密封类(sealed class
)。
虚方法
基类中的方法用virtual
关键字声明,衍生类中使用override
来重写,从而提供该方法的新的实现:
1 | namespace System |
使用virtual
和override
可以声明多态性的方法。
一些注意事项:
- 虚方法不能私有。虚方法的目的就是通过继承向其它类公开。类似地重写方法也不能私有,因为类不能改变它继承的方法的保护级别。但是重写方法可以用
protected
关键字实现所谓的“受保护”保密性; - 虚方法和重写方法必须有完全一致的签名。相同的函数名称、参数类型、数量、相同类型的返回值;
- 只能重写虚方法。对基类的非虚方法进行重写会出现编译时错误;
- 如果衍生类不用
override
关键字声明,则不是重写方法,而是隐藏,即成为和基类方法完全无关的另一个方法,该方法只是恰巧和基类方法同名。若如此做会出现编译时警告,称该方法会隐藏继承的同名方法,使用new
关键字可以消除此警告; - 重写方法隐式地成为虚方法,可以在衍生类中被重写;
接口
如System
内定义的IComparable
接口:
1 | interface IComparable |
声明接口
- 接口中不能包含字段;
- 声明时不要加任何访问修饰符;
- 接口内的方法没有实现,而是用一个分号代替;
实现接口
类或结构实现接口时,必须实现接口中的所有方法;
函数名、返回值类型,参数列表,参数的类型或关键字都完全匹配;
用于实现接口的所有方法都必须有
public
可访问性(后边提到的“显式接口实现”是一个特例,不需要访问修饰符);类可以有一个基类,但是可以实现多个接口,声明类时在冒号之后,基类写在接口的前边:
1
2
3
4
5
6
7
8
9
10
11
12interface ILandBound
{
int NumberOfLegs();
}
class Mammal
{
// ...
}
class Horse : Mammal,ILandBound
{
//...
}
通过接口来引用类
类似于基类对象引用衍生类的实例;
1
2Horse h = new Horse(...);
ILandBound i = h;
可以用
is
运算符来判断某个对象是否是实现了指定接口的类的一个实例;1
2
3
4
5if(h is ILandBound)
{
ILandBound i = h;
// ...
}
使用多个接口
1 | interface IGrazable |
显式接口实现
- 某个类实现的多个接口中有相同的函数,但是这些函数表达的不同的语义:
1 | interface IJourney |
接口的限制
- 不能在接口中包含任何字段;
- 不能在接口中定义任何的构造器;
- 不能在接口中定义任何的析构器;
- 不能为任何方法指定访问修饰符,接口中的方法都隐式为公有方法;
- 不能在接口中嵌套任何类型(如枚举、结构、类或其它接口);
- 接口可以继承接口,但是接口不能继承结构或类;
抽象类
为了明确声明不允许创建某个类的实例,必须将那个类声明为抽象类,用abstract
关键字实现的。
1 | abstract class GrazingMammal : Mammal, IGrazable |
抽象方法
抽象类中可以包含抽象方法,使用abstract
关键字修饰:
- 抽象方法原则上与虚方法相似,只是它不包含方法主体;
- 派生类必须重写抽象方法;
- 抽象方法不可以私有;
密封类
使用sealed
关键字指定某个类为密封类,即该类不能作为基类衍生出新的类。
1 | sealed class Horse : GrazingMammal, ILandBound |
- 密封类中不能声明任何虚方法;
- 抽象类不能密封;
密封方法
可以用sealed
关键字声明非密封类中的一个单独的方法是密封的,即衍生类不能重写该方法。只有用override
关键字声明的方法才能密封,而且方法要声明为sealed override
。
以下是interface
、virtual
、override
和sealed
关键字的一种理解方式:
interface
引入方法的名称virtual
方法是方法的第一个实现override
方法是方法的另一个实现sealed
是方法的最后一个实现
小结
接口、类和结构定义方法时,各种有效(yes)、无效(no)或必须(required)的关键字组合:
关键字 | 接口 | 抽象类 | 类 | 密封类 | 结构 |
---|---|---|---|---|---|
abstract | no | yes | no | no | no |
new | yes[1] | yes | yes | yes | no[2] |
override | no | yes | yes | yes | no[3] |
private | no | yes | yes | yes | yes |
protected | no | yes | yes | yes | no[4] |
public | no | yes | yes | yes | yes |
sealed | no | yes | yes | required | no |
virtual | no | yes | yes | no | no |
备注
- [1] 接口可以扩展另一个接口,并引入一个具有相同签名的新方法
- [2] 结构不支持继承,所以不能隐藏方法
- [3] 结构不支持继承,所以不能重写方法
- [4] 结构不支持继承,但结构隐式密封,所以不能从它派生