首页 > 代码库 > 第七章 继承和多态
第七章 继承和多态
1 继承
1.1 基类与派生类
C#中的类不支持多继承。只有在类和接口之间可以实现多继承。
public static void Main() { Business c1 = new Business(); c1.Name = "李明"; c1["办公电话"] = "07188888888"; c1.Output(); ClassMate c2 = new ClassMate(); c2.Name = "张鹏"; c2.Birthday = new DateTime(1978, 12, 31); c2.Output(); }
/// <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; } } //索引函数 public string this[string sType] { get { string type = sType.ToUpper(); switch (type) { case "住宅电话": return m_homePhone; case "办公电话": return m_busiPhone; case "手机": return m_mobilePhone; default: return null; } } set { string type = sType.ToUpper(); switch (type) { case "住宅电话": m_homePhone = value; break; case "办公电话": m_busiPhone = value; break; case "手机": m_mobilePhone = value; break; default: throw new ArgumentOutOfRangeException(); } } } //构造函数 public Contact() { } //方法 public void Output() { Console.WriteLine("姓名:{0}", m_name); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine(); } }
/// <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> /// 派生类:同学ClassMate /// </summary> public class ClassMate:Contact { //字段 protected DateTime m_birthday; //属性 public DateTime Birthday { get { return m_birthday; } set { m_birthday = value; } } }
1.2 隐藏基类的成员
通过new关键字来隐藏基类中的成员,来存储不同的信息或执行不同的操作。
new关键字放在访问限制修饰符的前后均可,但一定要在成员的类型说明之间
隐藏基类成员时所使用的new关键字属于一种修饰符,与创建对象的new操作符是完全不同的。
1.3 base关键字
this关键字可以用来访问当前对象,而base关键字则可以用来访问基类对象,调用基类对象的成员。
//方法public new void Output(){ base.Output(); Console.WriteLine("...");}
1.4 继承中的构造函数和析构函数
创建一个派生类的实例,在执行其构造函数代码之前,会隐式的调用基类的构造函数。
析构函数的调用顺序相反,首先调用当前对象的析构函数,依次调用各级基灶的析构函数。
对于不带参数的默认构造函数,如果基类中已经有了定义,派生类可以隐式的继承。一旦基类中定义了带参数的构造函数,派生类的继承就不是隐式的了,而是要提供参数数量和类型都是基类构造函数相同的构造函数。例如基类Contact中定义了下面的构造函数:
public Contact(string sName){ m_name=sName;}
那么下面的代码是错误的:
Business c1= new Business("李四");Classmate c2= new Classmate("张鹏");
下面是一个完整的示例:
public class NewInheritSample { static void Main() { //Business c2 = new Business();错误的代码 Business c1 = new Business("李明"); c1["办公电话"] = "01088888888"; c1["商务传真"] = "01066666666"; c1.Output(); ClassMate c2 = new ClassMate("张鹏"); c2.Birthday = new DateTime(1977, 02, 19); c2.Output(); } }
/// <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; } } //索引函数 public string this[string sType] { get { string type = sType.ToUpper(); switch (type) { case "住宅电话": return m_homePhone; case "办公电话": return m_busiPhone; case "手机": return m_mobilePhone; default: return null; } } set { string type = sType.ToUpper(); switch (type) { case "住宅电话": m_homePhone = value; break; case "办公电话": m_busiPhone = value; break; case "手机": m_mobilePhone = value; break; default: throw new ArgumentOutOfRangeException(); } } } //构造函数 public Contact() { } public Contact(string sName) { m_name = sName; } //方法 public void Output() { Console.WriteLine("姓名:{0}", m_name); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine(); } } /// <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; } } //索引函数 public new string this[string sType] { get { string type=sType.ToUpper(); switch(type) { case "商务传真": return m_busiFax; default: return base[sType]; } } set { string type = sType.ToUpper(); switch (type) { case "商务传真": m_busiFax = value; break; default: base[sType] = value; break; } } } //构造函数 public Business(string sName) : base(sName) { } //方法 public new void Output() { base.Output(); Console.WriteLine("商务传真:{0}", m_busiFax); Console.WriteLine(); } } /// <summary> /// 派生类:同学ClassMate /// </summary> public class ClassMate:Contact { //字段 protected DateTime m_birthday; //属性 public DateTime Birthday { get { return m_birthday; } set { m_birthday = value; } } //构造函数 public ClassMate(string sName) : base(sName) { } //方法 public new void Output() { base.Output(); Console.WriteLine("生日:{0}", m_birthday.ToString()); } }
2 多态性
2.1 虚拟方法和重载方法
派生类很少一成不变的去继承基类中的所有成员。一种情况是:派生类中的方法成员隐藏基类中同名的方法成员,通过new关键字对成员修饰。另一种更普遍和灵活的情况是:将基类的方法成员定义为虚拟方法,而在派生类中对虚拟方法进行重载。后者的优势在于它可以实现运行时的多态性。
基类的虚拟方法通过关键字virtual进行定义。下面是一个完整的例子:
class VirtualInheritSample { static void Main(string[] args) { Business c1 = new Business("李明"); c1["办公电话"] = "01088888888"; c1["商务传真"] = "01066666666"; ClassMate c2 = new ClassMate("张鹏"); c2.Birthday = new DateTime(1977, 02, 19); Contact c = c1; c.Output();//for Business c = c2; c.Output();//for Classmate } } /// <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; } } //虚拟索引函数 public virtual string this[string sType] { get { string type = sType.ToUpper(); switch (type) { case "住宅电话": return m_homePhone; case "办公电话": return m_busiPhone; case "手机": return m_mobilePhone; default: return null; } } set { string type = sType.ToUpper(); switch (type) { case "住宅电话": m_homePhone = value; break; case "办公电话": m_busiPhone = value; break; case "手机": m_mobilePhone = value; break; default: throw new ArgumentOutOfRangeException(); } } } //构造函数 public Contact() { } public Contact(string sName) { m_name = sName; } //虚拟方法 public virtual void Output() { Console.WriteLine("姓名:{0}", m_name); Console.WriteLine("住宅电话:{0}", m_homePhone); Console.WriteLine("办公电话:{0}", m_busiPhone); Console.WriteLine("手机:{0}", m_mobilePhone); Console.WriteLine(); } } /// <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; } } //重载索引函数 public override string this[string sType] { get { string type=sType.ToUpper(); switch(type) { case "商务传真": return m_busiFax; default: return base[sType]; } } set { string type = sType.ToUpper(); switch (type) { case "商务传真": m_busiFax = value; break; default: base[sType] = value; break; } } } //构造函数 public Business(string sName) : base(sName) { } //重载方法 public override void Output() { base.Output(); Console.WriteLine("商务传真:{0}", m_busiFax); Console.WriteLine(); } } /// <summary> /// 派生类:同学ClassMate /// </summary> public class ClassMate:Contact { //字段 protected DateTime m_birthday; //属性 public DateTime Birthday { get { return m_birthday; } set { m_birthday = value; } } //构造函数 public ClassMate(string sName) : base(sName) { } //重载方法 public override void Output() { base.Output(); Console.WriteLine("生日:{0}", m_birthday.ToString()); } }
提示:
--如果派生类中使用override关键字定义了重载方法,那么也就允许该类自己的派生类继续重载这个方法,因此重载方法也默认是一种虚拟方法,但不能同时使用virtual和override修饰一个方法。
--虚拟方法可以在派生类中重载,因此虚拟方法不能是私有的
--在基类和派生类中,对同一个虚拟方法和重载方法的访问限制应当相同,要么都使用public,要么都使用protected。
2.2 抽象类和抽象方法
第七章 继承和多态