C#面向对象程序设计 $7 继承和多态性
继承和多态性 继承
继承 基类和派生类 图形 三角形 四边形 多边形 椭圆形 平行四边形 圆形 菱形 矩形
类的构造方式 自顶向下 自底向上 从基础类型开始向下分解,不断得到新的派生类型。 联系人 -> 亲属、同事、同学 联系人 -> 亲属、同事、同学 自底向上 对现有的一组具体类型进行抽象,得到新的基础类型。 储蓄卡、信用卡、借记卡 -> 银行卡
继承方式构造类 /// <summary> /// 基类:联系人Contact /// </summary> public class Contact { //字段 protected string m_name; protected string m_homePhone = "未知"; protected string m_busiPhone = "未知"; protected string m_mobilePhone = "未知"; //属性 public string Name get { return m_name; } set { m_name = value; } } /// <summary> /// 派生类:商务Business /// </summary> public class Business : Contact { //字段 protected string m_busiFax = "未知"; protected string m_title = "女士/先生"; //属性 public string Title get { return m_title; } set { m_title = value; } }
继承方式构造类(续) /// <summary> /// 派生类:亲属Family /// </summary> public class Family : Contact { //字段 protected string m_relation; protected DateTime m_birthday; //属性 public string Relation get { return m_relation; } set { m_relation = value; } } public DateTime Birthday get { return m_birthday; } set { m_birthday = value; }
继承 基类和派生类 继承基类中的public/protected成员 增加新的成员 覆盖已有的成员(new)
成员覆盖示例 base关键字访问基类成员 //索引函数 public new string this[string phoneType] { get switch (phoneType) case "住宅电话": return m_homePhone; case "办公电话": return m_busiPhone; case "手机": return m_mobilePhone; default: return null; } set { switch (phoneType) case "住宅电话": m_homePhone = value; break; case "办公电话": m_busiPhone = value; case "手机": m_mobilePhone = value; default: } base关键字访问基类成员
继承 对象生命周期 派生类在创建时自顶向下地调用各级基类的构造函数,最后调用自身的构造函数; 销毁时首先调用自身的析构函数,而后自底向上地调用各级类类的析构函数。
对象生命周期示例 调用Grandsire的构造函数 调用Father的构造函数 调用Son的构造函数 调用Son的析构函数 public class BaseLifeSample { public static void Main() Son s1 = new Son(); System.GC.Collect(); } public class Grandsire public Grandsire() Console.WriteLine("调用Grandsire的构造函数"); ~Grandsire() Console.WriteLine("调用Grandsire的析构函数"); public class Father : Grandsire { public Father() Console.WriteLine("调用Father的构造函数"); } ~Father() Console.WriteLine("调用Father的析构函数"); public class Son : Father public Son() Console.WriteLine("调用Son的构造函数"); ~Son() Console.WriteLine("调用Son的析构函数"); 调用Grandsire的构造函数 调用Father的构造函数 调用Son的构造函数 调用Son的析构函数 调用Father的析构函数 调用Grandsire的析构函数
继承和多态性 多态性
多态性 多态性是指同一事物在不同条件下表现出不同的形态。 编译时的多态性 编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。 运行时的多态性 运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。 编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。
多态性 virtual/override:虚拟/重载 abstract/override :抽象/重载 sealed :密封,禁止重载 new: 编译时的多态性 override: 运行的多态性
new实现多态 Animal eat... Cat eat... public class Animal { public virtual void Eat() { Console.WriteLine("Animal eat"); } } public class Cat : Animal { public new void Eat() { Console.WriteLine("Cat eat"); } } class Tester { static void Main(string[] args) { Animal a = new Animal(); a.Eat(); Animal ac = new Cat(); ac.Eat(); Cat c = new Cat(); c.Eat(); } } Animal eat... Cat eat... 派生类Cat的Eat()方法使用new修饰时,Cat的对象转换为Animal对象后,调用的是Animal类中的Eat()方法。 使用new关键字后,使得Cat中的Eat()方法和Animal中的Eat()方法成为毫不相关的两个方法,只是它们的名字碰巧相同而已。
virtual / override实现多态 public class Animal { public virtual void Eat() { Console.WriteLine("Animal eat"); } } public class Dog : Animal public override void Eat() Console.WriteLine("Dog eat"); public class WolfDog : Dog Console.WriteLine("WolfDog eat"); class Tester { static void Main(string[] args) { Animal[] animals = new Animal[3]; animals[0] = new Animal(); animals[1] = new Dog(); animals[2] = new WolfDog(); for (int i = 0; i < 3; i++) { animals[i].Eat(); } } } Animal eat... Dog eat... WolfDog eat... 如果基类中的一个方法使用virtual修饰,在用派生类的对象实例化的基类对象后,该基类对象调用这个方法会自动调用派生类中的重载方法。
abstract-override实现多态 public abstract class Animal { public abstract void Eat(); } public class Cat : Animal public override void Eat() Console.WriteLine("Cat eat"); public class Dog : Animal Console.WriteLine("Dog eat"); public class WolfDog : Dog { public override void Eat() Console.WriteLine("Wolfdog eat"); } class Tester static void Main(string[] args) Animal[] animals = new Animal[3]; animals[0] = new Cat(); animals[1] = new Dog(); animals[2] = new WolfDog(); for (int i = 0; i < animals.Length; i++) animals[i].Eat(); Cat eat... Dog eat... Wolfdog eat... 通过使用abstract-override可以和virtual-override一样地实现多态。不同之处在于,包含虚拟方法的类可以被实例化,而包含抽象方法的类不能被实例化。
抽象类与抽象方法 抽象方法一定属于抽象类,但抽象类不一定要包含抽象方法。 抽象方法必须重载。 抽象类无法创建实例,而是强制要求通过派生类来实现其功能。
密封类与密封方法 密封类不允许派生出其它类 密封方法不允许被重载 密封方法本身也要求是一个重载方法
密封类与密封方法示例 //程序清单P7_6.cs: using System; namespace P7_6 { public class SealedSample public static void Main() A a = new A1(); a.Output(); a = new A2(); a = new A1_1(); a = new A1_2(); } public abstract class A public abstract void Output(); public class A1 : A { public override sealed void Output() Console.WriteLine("A1"); } public sealed class A2 : A public override void Output() Console.WriteLine("A2"); public class A1_1 : A1 public class A1_2 : A1 public new void Output() Console.WriteLine("A1_2");