3.5 重 写
派生类可以对基类的行为进行重写,重写使得派生类型可以在适当的地方改变其实现方法。Employee类中的Pay方法在CommissionedEmployee类中被重写,以增加在每张付薪水的支票上的授权。在SalariedEmployee类中,Pay方法被重写以通过分配每年的收入来计算薪水。方法、属性和索引器都可以被重写。基类的字段和静态成员不能在派生类中被重写。但是派生类型可以隐藏基类的字段和静态成员。
在什么时候允许一个子类对其父类进行重写?大部分父类都希望子类能够完全继承其行为,但是父类的成员仅仅是对子类的一种建议。子类倾向于重写父类的行为,父类对此毫无办法。在很多面向对象的语言中都采用这种模式。子类或派生类可以对父类或基类的行为进行重写。派生类可能会不适当地重写基类的行为从而导致对基类的破坏。这被称作是脆弱基类问题:这个问题在类库中表现得尤其明显,因为在类库中,子类很可能会继承一个类型并且不知不觉地或不正确地重写一个基类成员。在C#中采用的是不同的模型。在C#中,默认情况下,函数不能被重写。父类必须默认对方法的重写,此外,子类必须承认它要对方法进行重写的意图。这就防止子类在不知情的情况下对基类成员进行重写。
3.5.1 virtual和override关键字
virtual关键字表示一个成员可以在一个子类中被重写。它可以用在方法、属性、索引器和事件上。在派生类中,override关键字表示要对基类的一个虚成员进行重写的意图。virtual和override关键字互为补充。virtual关键字是可以传播到后代中去的。一个虚方法可以在派生类和后代类中被重写。
重写并不属于要么全有要么全无的情况。通过采用base关键字,基类的公共和被保护成员在派生类中是可访问的,其语法是base.member。在使用base关键字的时候不能跳层。例如,你不能访问一个祖父类中的成员。base.base.member这样的语法是非法的。在以下代码中,SalariedEmployee.Pay方法调用Employee.Pay方法来在付薪水的支票上显示一个雇员的名字:
public class Employee: ICloneable {
public Employee() {
}
public Employee(int id) {
if((id<1000) || (id>9999)) {
throw new Exception(
"Invalid Employee ID");
}
propID=id;
}
public virtual void Pay() {
Console.WriteLine("Employee: "+FullName);
}
// partial listing
}
public class SalariedEmployee: Employee {
public override void Pay() {
base.Pay();
Console.WriteLine("Pay is: {0,6:c}",
propSalary/(decimal)propPeriods);
}
// partial listing
}
3.5.2 重载与重写
重写和重载是两个不同的概念。当一个基类的一个成员被重写时,基成员和派生成员的签名是相同的。而重载需要不同的签名。在一个派生类中,一个函数可以被重载、重写,或既重载又重写。当一个成员以不同的签名被重写的时候,在派生类中会产生一个编译器警告。这防止在希望方法重写的地方意外发生重载,如以下代码所示。在代码中,MethodA在YClass类中被重载而不是重写。基类和派生类的实现都在Main函数中被调用。
using System;
namespace Donis.CSharpBook{
public class Starter{
public static void Main(){
YClass obj=new YClass();
obj.MethodA();
obj.MethodA(10);
}
}
public class ZClass {
public virtual void MethodA() {
Console.WriteLine("ZClass.MethodA");
}
}
public class YClass: ZClass
{
public override void MethodA(int a) {
Console.WriteLine("YClass.MethodA");
}
}
}
3.5.3 重写事件
出于完整性考虑,以下是重写一个事件的示例代码。事件将在第8章中讨论。
public class ZClass {
public virtual void MethodA() {
}
public delegate void MyDel();
public virtual event MyDel MyEvent {
add{
}
remove{
}
}
}
public class YClass: ZClass {
public override event MyDel MyEvent{
add{
// different implementation
}
remove{
// different implementation
}
}
}






