继承和多态
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.
C#继承与多态:理解virtual, override, new与abstract的关键区别
1666

被折叠的 条评论
为什么被折叠?



