3. 第一章:C#面向对象编程之继承和多态
第一章:C#面向对象编程
第三节:继承和多态
*继承可以让class具有一种特殊的能力,即实现class本身定义的所有功能外,还可以对父类(或称为基类、超类)的某些属性和功能进行扩展,这样的类我们称之为子类(或派生类)。
继承有如下的几个规则:
1. 可传递性:
如: C是B的子类,B是A的子类,那么C会同时继承B和A;(object为所有类的基类)
2. 唯扩展性:
子类应是对父类的扩展,而不能在子类除去父类里面的定义;
3. 构造函数和析构函数不能被继承:
除去构造函数和析构函数不能被继承,其他的成员都能被继承(注意区别:能被继承和能被访问,是两个概念)
4. 重载屏蔽性:
如果子类定义了和父类成员同名的新成员,那么会执行重载和覆盖逻辑。但要明白的是,这样做,并不是删除了父类的这些成员,而是不能再访问这些被重载和覆盖的成员而已。
5. 子类可以从父类的虚方法、虚属性以及虚索引器进行重载(即多态)
我们可以借用override关键字对父类的虚方法、虚属性以及虚索引器进行重载;
同时,我们也可以借用override关键字对抽象父类的抽象方法进行重载。
abstract和virtural的区别:
(1) abstract可以修饰class以及abstract class内部的函数、属性和索引器;而virtual不可以修饰class;
(2) abstract修饰内部的函数、属性和索引器的时候,必须在abstract class才可以;而且abstract函数、属性和索引器不可以在abstract class里面有实现,但virtual必须要有实现。
(3) virtual既可以修饰abstract class里面的函数、属性和索引器,也可以修饰一般class里面的函数、属性和索引器;
*基础知识扩展:什么是索引器?
索引器允许类或者结构的实例按照与数组相同的方式进行索引取值,索引器与属性类似,不同的是索引器的访问是带参的。
A. 索引器和数组比较:
(1)索引器的索引值(Index)类型不受限制;
(2)索引器允许重载;
(3)索引器不是一个变量;
B. 索引器和属性的不同点
(1)属性以名称来标识,索引器以函数形式标识;
(2)索引器可以被重载,属性不可以;
(3)索引器不能声明为static,属性可以;
下面一个简单的例子可见一斑:
1 public class MyClass 2 { 3 private string[] _strArray; 4 5 public MyClass(int length) 6 { 7 this._strArray = new string[length]; 8 } 9 10 public string this[int index] 11 { 12 get 13 { 14 if (index < this._strArray.Length) 15 { 16 return this._strArray[index]; 17 } 18 return null; 19 } 20 set 21 { 22 if (index < this._strArray.Length) 23 { 24 this._strArray[index] = value; 25 } 26 } 27 } 28 }
1 static void Main(string[] args) 2 { 3 MyClass myClass = new MyClass(3); 4 myClass[0] = "A"; 5 myClass[1] = "B"; 6 myClass[2] = "C"; 7 8 Console.WriteLine(myClass[0]); 9 Console.WriteLine(myClass[1]); 10 Console.WriteLine(myClass[2]); 11 12 Console.ReadLine(); 13 }
运行结果:
A
B
C
6. 子类只能继承一个父类(class),而可以继承多个接口(interface)(多重继承)
*多态性:同一操作作用于不同的类的实例,将产生不同的执行结果,即不同类的对象收到相同的消息时,得到不同的结果。
下图所示的类之间继承关系和多态用法,可以让大家更好了解这些概念的意义:
具体的实现代码:
1 public abstract class Creature 2 { 3 private string _spiece; 4 5 public Creature(string spiece) 6 { 7 this._spiece = spiece; 8 } 9 10 public string Spiece 11 { 12 get { return this._spiece; } 13 } 14 15 public abstract void Breath(); 16 }
1 public class Animal : Creature 2 { 3 private bool? _sex; 4 5 public Animal(string spiece) 6 : base(spiece) 7 { 8 this._sex = null; 9 } 10 11 public bool? Sex 12 { 13 get { return _sex; } 14 } 15 16 public virtual void Run() 17 { 18 Console.WriteLine("I am running..."); 19 } 20 21 public override void Breath() 22 { 23 Console.WriteLine("I am breathing by animal respiration..."); 24 } 25 }
1 public class Monkey : Animal 2 { 3 public Monkey(string spiece) 4 : base(spiece) { } 5 6 public void ClimbTree() 7 { 8 Console.WriteLine("I am climbing tree..."); 9 } 10 11 public override void Run() 12 { 13 Console.WriteLine("I am running with two legs, sometimes with four legs..."); 14 } 15 16 public override void Breath() 17 { 18 Console.WriteLine("I am breathing with lung..."); 19 } 20 }
1 public class Fish : Animal 2 { 3 public Fish(string spiece) 4 : base(spiece) { } 5 6 public override void Run() 7 { 8 Console.WriteLine("I am running in the water with fins..."); 9 } 10 11 public override void Breath() 12 { 13 Console.WriteLine("I am breathing with cheek..."); 14 } 15 }
1 public class Plant : Creature 2 { 3 public Plant(string spiece) 4 : base(spiece) { } 5 6 public override void Breath() 7 { 8 Console.WriteLine("I am breathing by plant respiration..."); 9 } 10 }
1 public class Tree : Plant 2 { 3 public Tree(string spiece) 4 : base(spiece) { } 5 6 //重载Breath()内部调用base.Breath(),其实和不写没啥区别,这里只是想告诉大家本质是什么。 7 public override void Breath() 8 { 9 base.Breath(); 10 } 11 }
程序运行测试代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Creature creature001 = new Animal("Animal"); 6 Console.WriteLine(creature001.Spiece); 7 creature001.Breath(); 8 9 Console.WriteLine("—————————————————————————"); 10 11 Creature creature002 = new Plant("Plant"); 12 Console.WriteLine(creature002.Spiece); 13 creature002.Breath(); 14 15 Console.WriteLine("—————————————————————————"); 16 17 Animal animal001 = new Animal("Animal", true); 18 Console.WriteLine(animal001.Spiece); 19 Console.WriteLine(string.Format("Spiece:{0}; Sex:{1}", animal001.Spiece, GetSexName(animal001.Sex))); 20 animal001.Breath(); 21 animal001.Run(); 22 23 Console.WriteLine("—————————————————————————"); 24 25 Creature monkey001 = new Monkey("Monkey", true); 26 Console.WriteLine(monkey001.Spiece); 27 monkey001.Breath(); 28 29 Console.WriteLine("—————————————————————————"); 30 31 Animal animal002 = new Monkey("Monkey", false); 32 Console.WriteLine(animal002.Spiece); 33 Console.WriteLine(string.Format("Spiece:{0}; Sex:{1}", animal002.Spiece, GetSexName(animal002.Sex))); 34 animal002.Breath(); 35 animal002.Run(); 36 37 Console.WriteLine("—————————————————————————"); 38 39 Creature fish001 = new Fish("Fish", true); 40 Console.WriteLine(fish001.Spiece); 41 monkey001.Breath(); 42 43 Console.WriteLine("—————————————————————————"); 44 45 Animal fish002 = new Fish("Fish", true); 46 Console.WriteLine(fish001.Spiece); 47 Console.WriteLine(string.Format("Spiece:{0}; Sex:{1}", fish002.Spiece, GetSexName(fish002.Sex))); 48 fish002.Breath(); 49 fish002.Run(); 50 51 Console.WriteLine("—————————————————————————"); 52 53 Animal animal004 = new Monkey("Monkey", false); 54 Console.WriteLine(animal004.Spiece); 55 Console.WriteLine(string.Format("Spiece:{0}; Sex:{1}", animal004.Spiece, GetSexName(animal004.Sex))); 56 animal002.Breath(); 57 animal002.Run(); 58 59 Console.WriteLine("—————————————————————————"); 60 61 Monkey jack = new Monkey("Monkey", true); 62 Console.WriteLine(jack.Spiece); 63 Console.WriteLine(string.Format("Spiece:{0}; Sex:{1}", jack.Spiece, GetSexName(jack.Sex))); 64 jack.Breath(); 65 jack.Run(); 66 jack.ClimbTree(); 67 68 Console.ReadLine(); 69 } 70 71 private static string GetSexName(bool? value) 72 { 73 string sexName = null; 74 if (value == null) 75 { 76 sexName = "undefined"; 77 } 78 else 79 { 80 sexName = value.Value ? "male" : "female"; 81 } 82 return sexName; 83 } 84 }
运行结果:
Animal
I am breathing by animal respiration...
—————————————————————————
Plant
I am breathing by plant respiration...
—————————————————————————
Animal
Spiece:Animal; Sex:male
I am breathing by animal respiration...
I am running...
—————————————————————————
Monkey
I am breathing with lung...
—————————————————————————
Monkey
Spiece:Monkey; Sex:female
I am breathing with lung...
I am running with two legs, sometimes with four legs...
—————————————————————————
Fish
I am breathing with lung...
—————————————————————————
Fish
Spiece:Fish; Sex:male
I am breathing with cheek...
I am running in the water with fins...
—————————————————————————
Monkey
Spiece:Monkey; Sex:female
I am breathing with lung...
I am running with two legs, sometimes with four legs...
—————————————————————————
Monkey
Spiece:Monkey; Sex:male
I am breathing with lung...
I am running with two legs, sometimes with four legs...
I am climbing tree...
*多重继承
类和接口都可以继承接口,而且可以继承不止一个接口。
需求:
IDraw接口,定义DrawLine、DrawCircle和DrawRectangle方法;
Drawer,定义为抽象类,实现IDraw接口,附加属性Name;
ImpressionistDrawer(印象画派画家)类,继承Drawer抽象类;
RealismDrawer(写实主义画派画家)类,继承Drawer抽象类。
如此,代码如下:
1 public interface IDraw 2 { 3 void DrawLine(); 4 5 void DrawCircle(); 6 7 void DrawRectangle(); 8 }
1 public abstract class Drawer : IDraw 2 { 3 private string _name; 4 5 public Drawer(string name) 6 { 7 this._name = name; 8 } 9 10 public string Name 11 { 12 get 13 { 14 return this._name; 15 } 16 } 17 18 public abstract void DrawLine(); 19 20 public abstract void DrawCircle(); 21 22 public abstract void DrawRectangle(); 23 }
1 //印象画派画家 2 public class ImpressionistDrawer : Drawer 3 { 4 public ImpressionistDrawer(string name) 5 : base(name) { } 6 7 public override void DrawLine() 8 { 9 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a line with pen.", this.Name)); 10 } 11 12 public override void DrawCircle() 13 { 14 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a circle with pen.", this.Name)); 15 } 16 17 public override void DrawRectangle() 18 { 19 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a rectangle with pen.", this.Name)); 20 } 21 }
1 //写实主义画派画家 2 public class RealismDrawer : Drawer 3 { 4 public RealismDrawer(string name) 5 : base(name) { } 6 7 public override void DrawLine() 8 { 9 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a line with pencil.", this.Name)); 10 } 11 12 public override void DrawCircle() 13 { 14 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a circle with pencil.", this.Name)); 15 } 16 17 public override void DrawRectangle() 18 { 19 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a rectangle with pencil.", this.Name)); 20 } 21 }
调用:
1 static void Main(string[] args) 2 { 3 IDraw draw001 = new RealismDrawer("莫柰"); 4 draw001.DrawCircle(); 5 draw001.DrawLine(); 6 draw001.DrawRectangle(); 7 8 Console.WriteLine(); 9 10 IDraw draw002 = new ImpressionistDrawer("弗洛伊德"); 11 draw002.DrawCircle(); 12 draw002.DrawLine(); 13 draw002.DrawRectangle(); 14 15 Console.ReadLine(); 16 }
运行结果:
The realism drawer:莫柰 is drawing a circle with pencil.
The realism drawer:莫柰 is drawing a line with pencil.
The realism drawer:莫柰 is drawing a rectangle with pencil.
The impressionist drawer:弗洛伊德 is drawing a circle with pen.
The impressionist drawer:弗洛伊德 is drawing a line with pen.
The impressionist drawer:弗洛伊德 is drawing a rectangle with pen.
扩展一下,比方说:这些画家除了要画画之外,一样都要吃饭睡觉打豆豆,怎么处理呢?
我们当然不能把吃饭睡觉打豆豆归为IDraw这个intercace里面,因为这完全是不相干的行为呀!于是,我们考虑新建一个interface
1 public interface ILive 2 { 3 void EatRice(); 4 5 void Sleep(); 6 7 void BeatDouDou(); 8 }
1 public abstract class Drawer : IDraw, ILive 2 { 3 private string _name; 4 5 public Drawer(string name) 6 { 7 this._name = name; 8 } 9 10 #region IDraw 11 public string Name 12 { 13 get 14 { 15 return this._name; 16 } 17 } 18 19 public abstract void DrawLine(); 20 21 public abstract void DrawCircle(); 22 23 public abstract void DrawRectangle(); 24 25 #endregion 26 27 #region ILive 28 29 public abstract void EatRice(); 30 31 public abstract void Sleep(); 32 33 public abstract void BeatDouDou(); 34 35 #endregion 36 }
1 public ImpressionistDrawer(string name) 2 : base(name) { } 3 4 public override void DrawLine() 5 { 6 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a line with pen.", this.Name)); 7 } 8 9 public override void DrawCircle() 10 { 11 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a circle with pen.", this.Name)); 12 } 13 14 public override void DrawRectangle() 15 { 16 Console.WriteLine(string.Format("The impressionist drawer:{0} is drawing a rectangle with pen.", this.Name)); 17 } 18 19 public override void EatRice() 20 { 21 Console.WriteLine(string.Format("The impressionist drawer:{0} is eating rice.", this.Name)); 22 } 23 24 public override void Sleep() 25 { 26 Console.WriteLine(string.Format("The impressionist drawer:{0} is sleeping.", this.Name)); 27 } 28 29 public override void BeatDouDou() 30 { 31 Console.WriteLine(string.Format("The impressionist drawer:{0} is beating DouDou.", this.Name)); 32 } 33 }
1 public class RealismDrawer : Drawer 2 { 3 public RealismDrawer(string name) 4 : base(name) { } 5 6 public override void DrawLine() 7 { 8 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a line with pencil.", this.Name)); 9 } 10 11 public override void DrawCircle() 12 { 13 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a circle with pencil.", this.Name)); 14 } 15 16 public override void DrawRectangle() 17 { 18 Console.WriteLine(string.Format("The realism drawer:{0} is drawing a rectangle with pencil.", this.Name)); 19 } 20 21 public override void EatRice() 22 { 23 Console.WriteLine(string.Format("The realism drawer:{0} is eating rice.", this.Name)); 24 } 25 26 public override void Sleep() 27 { 28 Console.WriteLine(string.Format("The realism drawer:{0} is sleeping.", this.Name)); 29 } 30 31 public override void BeatDouDou() 32 { 33 Console.WriteLine(string.Format("The realism drawer:{0} is beating DouDou.", this.Name)); 34 } 35 }
调用:
1 static void Main(string[] args) 2 { 3 IDraw drawer001 = new RealismDrawer("莫柰"); 4 drawer001.DrawCircle(); 5 drawer001.DrawLine(); 6 drawer001.DrawRectangle(); 7 8 Console.WriteLine(); 9 10 IDraw drawer002 = new ImpressionistDrawer("弗洛伊德"); 11 drawer002.DrawCircle(); 12 drawer002.DrawLine(); 13 drawer002.DrawRectangle(); 14 15 Console.WriteLine(); 16 17 ILive drawer003 = new RealismDrawer("莫柰"); 18 drawer003.EatRice(); 19 drawer003.Sleep(); 20 drawer003.BeatDouDou(); 21 22 Console.WriteLine(); 23 24 ILive drawer004 = new RealismDrawer("弗洛伊德"); 25 drawer004.EatRice(); 26 drawer004.Sleep(); 27 drawer004.BeatDouDou(); 28 29 Console.ReadLine(); 30 }
运行结果:
The realism drawer:莫柰 is drawing a circle with pencil.
The realism drawer:莫柰 is drawing a line with pencil.
The realism drawer:莫柰 is drawing a rectangle with pencil.
The impressionist drawer:弗洛伊德 is drawing a circle with pen.
The impressionist drawer:弗洛伊德 is drawing a line with pen.
The impressionist drawer:弗洛伊德 is drawing a rectangle with pen.
The realism drawer:莫柰 is eating rice.
The realism drawer:莫柰 is sleeping.
The realism drawer:莫柰 is beating DouDou.
The realism drawer:弗洛伊德 is eating rice.
The realism drawer:弗洛伊德 is sleeping.
The realism drawer:弗洛伊德 is beating DouDou.