继承和多态
virtual
必须重载吗? virtual
可以直接调用吗? 多个修饰符之间顺序有要求吗? virtual
必须完整定义吗? virtual
后使用override
和使用new
隐藏有什么区别?
在派生类中使用new
修饰符可以声明与基类中同名的方法和字段可以隐藏(覆盖)基类的方法和字段. 在基类的方法被隐藏的情况下, 调用派生类对象的方法究竟调用哪个类取决于对象声明的类型. 在c#中, 派生类其实是可以被声明成其基类, 可以跳过其被隐藏. 还用一种访问基类的被隐藏的方法的方法是在派生类中使用base
关键字.
如果不希望在使用对象时还操心对象到底在声明时使用的什么类型, 就需要在基类中使用virtual
关键字声明方法, 在派生类中使用override
修饰方法. 但virtual
修饰的对象可以直接通过基类的对象调用吗? 派生类可以不声明override
的方法(必须重载吗)吗? 基类中没有使用virtual
派生类中可以使用override
吗? 基类中没有abstact
和virtual
修饰符不可以被override
. vitural
修饰的方法可以直接通过基类的对象直接调用. 派生类可以不重写基类的虚拟方法, 当调用派生类对象的虚拟方法时, 对用的将会是其父类的虚拟方法. 基类中没有虚拟修饰符的方法是不可以被重写的.
abstract
用于修饰抽象类和抽象方法. 抽象类没有实例, 而且必须被继承. 抽象方法必须被重载, 重载为一个可以实现的方法或者一个新的抽象方法. 抽象方法定义时没有函数体, 但可以有参数吗? 可以直接使用new
覆盖吗? 抽象类里可以声明和调用普通方法和虚拟方法吗? 抽象类中的方法如果不声明abstract
是不能不写函数体的, 但这个方法如果不是静态的就不能直接通过类来调用. 换句话说, 通过类来调用的只能是静态成员. 抽象方法只能存在于抽象类中, 不可以在非抽象类中包含. 抽象方法定义时可以有参数, 但重写时需要保证参数类型和数量一致, 另外, 抽象方法不可以不注明返回类型. 基类的抽象方法不可以直接被new
覆盖. 可以通过抽象类调用其普通方法, 只不过由于其不能被实例化, 需要将方法声明为静态的, 再通过抽象类访问. 由于需要将方法标记为静态, 并且静态方法不可修饰为虚拟, 所以不可以通过抽象类调用虚拟方法.
sealed
用于修饰密封方法和密封类, 密封类不可被继承, 密封方法不可以被重载. 但要注意的是, 密封方法可以在其派生类中使用new
给隐藏.
需要注意的是, 构造函数的继承稍显复杂, 如果基类中有声明的带参数的构造函数就需要在派生类中显式继承:
// 基类
public class BaseClass
{
public BaseClass(type varName)
{
...
}
...
}
// 派生类
public class ChildClass : BaseClass
{
public ChildClass(type varName) : base(varName)
{
...
}
...
}
c#使用如此多的关键字可能是为了尽可能消除语言的二意性.
设计实验:
- 一个方法, 被直接使用
new
隐藏. 该方法直接被派生类调用, 该方法的对象放在基类的数组中再被调用. - 测试
virtual
的几个问题. - 直接调用
virtual
方法. - 尝试使用
new
隐藏. - 测试
abstract
的几个问题. - 编码机器抽象类和家用电器半抽象类. 机器抽象方法为启动和关闭. 家用电器继承自机器, 虚拟方法为待机. 设置家用电器数组, 存放空调, 电视实例.
- 使用6中的设计实现1-5.
public abstract class Machine
{
public Machine(string name)
{
this.name = name.Trim();
}
protected enum Statuses : byte
{
off, on, standby
}
protected string name;
public string Name
{
get { return name; }
set { name = value; }
}
protected Statuses status = Statuses.off;
public int StatusByte
{
get { return (byte)status; }
}
public string StatusString
{
get { return status.ToString(); }
}
public abstract void On();
public abstract void Off();
// public virtual static void Description()
// 错误 CS0112 静态成员不能标记为“virtual”
public static void Description()
{
Console.WriteLine("I am class Machine, I am an abstract class.");
}
}
public class ElectricalApp : Machine
{
public ElectricalApp(string name) : base(name){ }
// public new void On()
// 错误 CS0534 “ElectricalApp”不实现继承的抽象成员“Machine.On()”
public override void On()
{
if (this.status == Statuses.on)
{
Console.WriteLine($"{this.name}: Already On.");
}
else
{
this.status = Statuses.on;
Console.WriteLine($"{this.name}: Turn On!");
}
}
public override void Off()
{
if (this.status == Statuses.off)
{
Console.WriteLine($"{this.name}: Already Off.");
}
else
{
this.status = Statuses.off;
Console.WriteLine($"{this.name}: Turn Off!");
}
}
// public abstract void Standby();
// 错误 CS0513 “ElectricalApp.Standby()”是抽象的, 但它包含在非抽象类型“ElectricalApp”中
public virtual void Standby()
{
if (this.status == Statuses.standby)
{
Console.WriteLine($"{this.name}: Already Standby.");
}
else
{
this.status = Statuses.standby;
Console.WriteLine($"{this.name}: Turn Standby.");
}
}
// public override void Test() { } // 在 Machine 中假设有public void Test(){}
// 错误 CS0506 “ElectricalApp.Test()”: 继承成员“Machine.Test()”未标记为 virtual、abstract 或 override, 无法进行重写
}
public class Television : ElectricalApp
{
public Television(string name) : base(name){ }
public override void Standby() // 注意是使用 override 进行方法重写
{
if (this.status == Statuses.standby)
{
Console.WriteLine($"{this.name}: Already Standby.");
}
else
{
this.status = Statuses.standby;
Console.WriteLine($"Now you can't see anything on {this.name} without Off.");
}
}
}
public class AirConditioner : ElectricalApp
{
public AirConditioner(string name) : base(name){ }
public new void Standby() // 注意是使用 new 进行方法隐藏
{
if (this.status == Statuses.standby)
{
Console.WriteLine($"{this.name}: Already Standby.");
}
else
{
this.status = Statuses.standby;
Console.WriteLine($"There's not any air out from {this.name}, but it can be turned on quickly.");
}
}
}
internal class Program
{
static void Main(string[] args)
{
// Machine machine = new Machine();
// 错误 CS0144 无法创建抽象类型或接口“Machine”的实例
// Machine.On(); // Machine 类中有非静态的public On 方法时
// 错误 CS0120 对象引用对于非静态的字段、方法或属性“Machine.On()”是必需的
Machine.Description();
// 可以通过抽象类本身调用其静态方法.
ElectricalApp myApp = new ElectricalApp("myApp");
AirConditioner myAC = new AirConditioner("myAC");
Television myTV = new Television("myTV");
ElectricalApp[] myElectricalApps = { myApp, myAC, myTV };
// ElectricalApp 数组, 其中的元素不一定是 ElectricalApp 类, 还有其派生类.
Console.WriteLine("\nNow test Television!");
myElectricalApps[2].Standby();
// 由于使用的虚拟方法重写, 数组的 ElectricalApp 类声明不影响其派生类方法的调用.
myTV.On();
myTV.Standby();
Console.WriteLine("\nNow test Air-conditioner!");
myElectricalApps[1].Standby();
// 使用 new 修饰符覆盖基类的方法, 当派生类被声明为基类时(这里体现在派生类存储与基类数组)
// 对用的是其被隐藏的基类方法.
myAC.On();
myAC.Standby();
}
}
上述实验输出为:
I am class Machine, I am an abstract class.
Now test Television!
Now you can't see anything on myTV without Off.
myTV: Turn On!
Now you can't see anything on myTV without Off.
Now test Air-conditioner!
myAC: Turn Standby.
myAC: Turn On!
There's not any air out from myAC, but it can be turned on quickly.