在前一篇文章中,我们讲了面向对象的特征之一(封装),那么今天我们来了解面向对象的剩余两大特性。
一、继承
1、定义
继承就是从父类中获取一些公开的成员,如方法和属性。C#中只允许继承一个父类,但允许继承多个接口。如果子类继承接口,则必须实现接口中定义的所有公开成员。
公开成员是指在父类中定义为public的成员(public的作用域可在子类中生效,而private作用域则不可)
子类继承父类:
子类拥有父类非Private的属性和功能;
子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能;
子类还可以以自己的方式实现父类的功能(方法重写)
2、继承的两大特性:
001. 单根性:一个子类不能同时继承多个父类。
002. 传递性:如果有一个类A继承与类B,又有一个类C继承与类A,那么类C也可以使用类B中非私有的字段,属性和方法。
3.子类构造执行流程?
解析: 第一步:先进入子类构造参数列表
第二步:进入父类对应的构造
第三步:执行父类构造的方法体
第四步:回到子类方法体执行.
4、优点
继承使得所有子类公共部分都放在父类中,使得代码得到共享,这就避免了重复。此外,继承可使得修改或扩展继承而来的实现都较为容易。
5、注意
Protected表示继承时子类可以对基类有完全访问权。也就是说protected修饰的类成员,对子类公开,段不对其他类公开。
实例:
1 private void button1_Click(object sender,EventArgs e) 2 3 { 4 5 Cat cat = new Cat("咪咪");//将类实例化 Cat cat声明一个Cat对象,对象名为cat;newCat()将此cat对象实例化 6 7 cat.ShoutNum = 5; //给属性赋值 8 9 MessageBox.Show(cat.Shout()); 10 11 } 12 13 class Animal //定义父类 14 15 { 16 17 protected string name =""; //注意变为Protected 子类可以继承 18 19 public Animal(string name) 20 21 { 22 23 this.name = name; 24 25 26 27 } 28 29 public Animal() 30 31 { 32 33 this.name = "无名"; 34 35 } 36 37 protected int shoutNum = 3; //变为protected 38 39 public int ShoutNum 40 41 { 42 43 get 44 45 { 46 47 return shoutNum; 48 49 } 50 51 set 52 53 { 54 55 shoutNum = value; 56 57 } 58 59 } 60 61 } 62 63 class Cat : Animal //注意变化 64 65 { 66 67 public Cat() : base() //子类构造方法需要调用父类同样参数类型的构造方法,用base关键字代表父类 68 69 { } 70 71 public Cat(string name):base(name) //注意变化 72 73 { } 74 75 public string Shout() 76 77 { 78 79 string result = ""; 80 81 for (int i = 0; i <shoutNum; i++) 82 83 result += "喵!"; 84 85 return "我的名字叫:" +name + "" + result; 86 87 } 88 89 } 90 91 class Dog : Animal //注意变化 92 93 { 94 95 public Dog() : base() 96 97 { } 98 99 public Dog(string name): base(name) 100 101 { } 102 103 public string shout() 104 105 { 106 107 string result = ""; 108 109 for (int i = 0; i <shoutNum; i++) 110 111 result += "汪!"; 112 113 return "我的名字叫:" +name + "" + result; 114 115 } 116 117 } 118 119 private void button2_Click(objectsender, EventArgs e) 120 121 { 122 123 Dog dog = new Dog("狗");//将类实例化 Cat cat声明一个Cat对象,对象名为cat;newCat()将此cat对象实例化 124 125 dog.ShoutNum = 5; //给属性赋值 126 127 MessageBox.Show(dog.shout()); 128 129 }
二、多态
1、定义
多态是指类可以有多种形态,通过修改可以形成多个实现方法。子类从父类继承时它会获得父类的所有方法、字段、属性、事件。多态表示不同的对象可以执行相同的操作,但要通过它们自己的实现代码来执行。通俗的说:不同的对象对于同一个指令做出的响应不同,就是多态。
若要改变父类的数据和行为,两种方式:可创建新的成员替换父级成员,或者可以重写虚拟的父级成员。
(1)若要替换父级成员,需要使用new关键字。下面是GetName方法的两种形态,一种是返回父级名称,一种是返回子级名称。
1 第一种:返回父级名称 2 3 class PerentClass //父类 4 5 { 6 7 public string GetName() 8 9 { 10 11 return"父级名称"; 12 13 } 14 15 } 16 17 第二种:返回子级名称 18 19 class SonClass:PerentClass //子类继承父类 20 21 { 22 23 public new string GetName() 24 25 { 26 27 return"子级名称"; 28 29 } 30 31 } 32
(2)重写虚拟的父级成员的两个条件:父级成员使用关键字“virtual”;子级成员使用“override”。
1 class PerentClass //父类 2 3 { 4 5 public virtual string GetName() 6 7 { 8 9 return"父级名称"; 10 11 } 12 13 } 14 15 16 17 class SonClass:PerentClass //子类继承父类 18 19 { 20 21 public override string GetName() 22 23 { 24 25 return"子级名称"; 26 27 } 28 29 }
(2):抽象方法实现多态
1 /// <summary> 2 /// 鸟类:基类 3 /// </summary> 4 public abstract class Bird 5 { 6 /// <summary> 7 /// 吃:抽象方法 8 /// </summary> 9 public abstract void Eat();10 }
1 /// <summary> 2 /// 喜鹊:子类 3 /// </summary> 4 public class Magpie:Bird 5 { 6 /// <summary> 7 /// 重写父类中Eat方法 8 /// </summary> 9 public override void Eat()10 {11 Console.WriteLine("我是一只喜鹊,我喜欢吃虫子~");12 }13 }
1 /// <summary> 2 /// 老鹰:子类 3 /// </summary> 4 public class Eagle:Bird 5 { 6 /// <summary> 7 /// 重写父类中Eat方法 8 /// </summary> 9 public override void Eat()10 {11 Console.WriteLine("我是一只老鹰,我喜欢吃肉~");12 }13 }
1 /// <summary> 2 /// 企鹅:子类 3 /// </summary> 4 public class Penguin:Bird 5 { 6 /// <summary> 7 /// 重写父类中Eat方法 8 /// </summary> 9 public override void Eat()10 {11 Console.WriteLine("我是一只小企鹅,我喜欢吃鱼~");12 }13 }
1 static void Main(string[] args) 2 { 3 //创建一个Bird基类数组,添加 Magpie对象,Eagle对象,Penguin对象 4 Bird[] birds = { 5 new Magpie(), 6 new Eagle(), 7 new Penguin() 8 }; 9 //遍历一下birds数组10 foreach (Bird bird in birds)11 {12 bird.Eat();13 }14 Console.ReadKey();15 }
执行结果:
2,抽象类可以被继承吗?
可以,不仅可以被普通类继承,也可以被抽象类继承
3,抽象方法可以有方法体吗?
不可以,连花括号都不能有
4,简述抽象方法和虚方法的区别?
1 //01.定义一个抽象类,用abstract 修饰 2 3 //02.抽象方法不能有方法体,甚至连{}都不能有 4 5 //03.抽象方法只能存在于抽象类中,但是抽象类中可以有非抽象方法 6 7 //04.抽象类不能实例化 8 9 //05.抽象类中抽象方法只是用来规定方法的形式(参数,返回值),约束子类方法的形式10 11 //06.抽象类中的抽象成员必须在子类中全部实现,除非子类也是抽象类12 13 //07.子类实现抽象方法的快捷键,Ctrl+.(没有输入法情况下 )14 15 //shift+alt+F1016 17 //08.抽象类不能用static修饰:如果是static,抽象就无法被继承,也就失去了抽象类本身的意义
5、注意
子类以父类省份出现
子类在工作中以自己的方式来实现
子类乙父类的身份出现时,子类特有的属性和方法不可同时使用
使用多态时,需了解:虚方法和方法重写
为了使子类的实例完全接替来自父类的成员,父类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加virtual来实现。
子类选择使用override关键字,将父类实现替换为自己的实现,这就是方法重写。
说了这么多,那么我们用一个具体案例实现一下吧!
程序效果图如下:








关键代码如下:
//父类 public abstract class Vehicle//抽象类 { public string Color { get; set; }//颜色 public double DailyRent { get; set; }//租金 public string LiceseNo { get; set; }//车牌号 public string Name { get; set; }//车名 public int RentDate { get; set; }//租用天数 public string RentUser { get; set; }//租用者 public int YearsOfService { get; set; }//使用时间 public Vehicle() { } public Vehicle(string color,double dailyRent,string liceseNo,string name,int yearOfSerivce) //父类中的带参数构造 { this.Color = color; this.DailyRent = dailyRent; this.LiceseNo = liceseNo; this.Name = name; this.YearsOfService = yearOfSerivce; } public abstract double CalPrice();//父类中计算租车金额的抽象方法 }
1 //子类继承父类Vehicle 2 class Car:Vehicle 3 { 4 public Car(){} 5 public Car(string color, double dailyRent, string liceseNo, string name,int yearOfSerivce) 6 : base(color,dailyRent,liceseNo,name,yearOfSerivce) //给父类中的各个属性复制 7 { 8 9 }10 public override double CalPrice()//重写父类中租车的抽象方法11 {12 double price = base.RentDate * base.DailyRent;13 return price;14 }15 }
1 //子类Trcuk继承父类Vehicle 2 public class Truck:Vehicle 3 { 4 private int load;//卡车的载重量 5 6 public int Load//封装字段 7 { 8 get { return load; } 9 set { load = value; }10 }11 public Truck(int load, string color, double dailyRent, string liceseNo, string name, int yearOfSerivce)12 : base(color, dailyRent, liceseNo, name, yearOfSerivce) 13 {14 this.Load = load;15 }16 public Truck() { }17 public override double CalPrice()//卡车类重写父类中计算租车金额的抽象方法18 {19 double price = base.RentDate * base.DailyRent;20 return price;21 }22 }
1 public partial class Form1 : Form 2 { 3 public Form1() 4 { 5 InitializeComponent(); 6 } 7 //保存可租用车集合信息 8 Dictionary<string, Vehicle> vehicles = new Dictionary<string, Vehicle>(); 9 //保存租出车辆信息 10 Dictionary<string, Vehicle> rentVehicles = new Dictionary<string, Vehicle>(); 11 /// <summary> 12 /// 初始化几个可租用车信息 13 /// </summary> 14 private void Initail() 15 { 16 Car car = new Car(); 17 car.LiceseNo = "京RO123456"; 18 car.Name = "奥迪A6"; 19 car.Color= "黑色"; 20 car.YearsOfService = 3; 21 car.DailyRent = 250; 22 Car car1 = new Car(); 23 car1.LiceseNo = "京B32197"; 24 car1.Name = "大众2000"; 25 car1.Color = "蓝色"; 26 car1.YearsOfService = 3; 27 car1.DailyRent = 450; 28 Truck truck = new Truck(); 29 truck.LiceseNo = "豫S46541"; 30 truck.Name = "东风"; 31 truck.Color = "蓝色"; 32 truck.YearsOfService = 2; 33 truck.DailyRent = 500; 34 truck.Load = 20; 35 vehicles.Add(car.LiceseNo,car); 36 vehicles.Add(car1.LiceseNo,car1); 37 vehicles.Add(truck.LiceseNo,truck); 38 } 39 private void Form1_Load(object sender, EventArgs e) 40 { 41 Initail(); 42 43 this.txtLoad.Enabled = false; 44 } 45 /// <summary> 46 /// 更新可租车辆数据 47 /// </summary> 48 private void UpdatelvRent() 49 { 50 this.lvRent.Items.Clear(); 51 52 foreach (Vehicle item in vehicles.Values)//循环遍历集合中的数据,添加到ListView空间中 53 { 54 ListViewItem lvitem = new ListViewItem(item.LiceseNo); 55 if (item is Car)//判断item是否是Car类形 56 { 57 lvitem.SubItems.Add(item.Name); 58 lvitem.SubItems.Add(item.Color); 59 lvitem.SubItems.Add(item.YearsOfService.ToString()); 60 lvitem.SubItems.Add(item.DailyRent.ToString()); 61 62 } 63 if (item is Truck) 64 { 65 lvitem.SubItems.Add(item.Name); 66 lvitem.SubItems.Add(item.Color); 67 lvitem.SubItems.Add(item.YearsOfService.ToString()); 68 lvitem.SubItems.Add(item.DailyRent.ToString()); 69 lvitem.SubItems.Add(((Truck)item).Load.ToString()); 70 } 71 72 this.lvRent.Items.Add(lvitem); 73 } 74 } 75 private void btnQueryRent_Click(object sender, EventArgs e)//点击刷新按钮事件,调用刷新租车信息方法 76 { 77 UpdatelvRent(); 78 } 79 80 private void btnRent_Click(object sender, EventArgs e)//点击租车按钮事件 81 { 82 if (this.lvRent.SelectedItems.Count > 0 )//判断是否是选中的租车信息 83 { 84 if ( this.txtRenter.Text == string.Empty)//租车者不能为空 85 { 86 MessageBox.Show("租用者不能为空!!"); 87 return; 88 } 89 foreach (Vehicle item in vehicles.Values) 90 { 91 if (item is Car)//判断item是否是Car类形 92 { 93 if (this.lvRent.SelectedItems[0].SubItems[0].Text == item.LiceseNo) 94 { 95 vehicles[this.lvRent.SelectedItems[0].SubItems[0].Text].RentUser = this.txtRenter.Text; 96 rentVehicles.Add(item.LiceseNo, item);//添加到租车车辆信息的集合中 97 vehicles.Remove(item.LiceseNo);//从可租车辆信息的集合中删除选中的车辆信息 98 MessageBox.Show("租车成功!!!"); 99 this.txtRenter.Text = "";100 break;101 }102 }103 if (item is Truck)//同理之上104 {105 if (this.lvRent.SelectedItems[0].SubItems[0].Text == ((Truck)item).LiceseNo)106 {107 vehicles[this.lvRent.SelectedItems[0].SubItems[0].Text].RentUser = this.txtRenter.Text;108 109 vehicles.Remove(((Truck)item).LiceseNo);110 rentVehicles.Add(((Truck)item).LiceseNo, item);111 MessageBox.Show("租车成功!!!");112 this.txtRenter.Text = "";113 break;114 }115 }116 117 }118 UpdatelvRent();//刷新数据119 }120 else 121 {122 MessageBox.Show("请选择一辆车租");123 }124 125 126 }127 128 private void btnExit_Click(object sender, EventArgs e)//点击退出按钮事件129 {130 this.Close();131 }132 /// <summary>133 /// 更新还车信息134 /// </summary>135 private void UpdateLvRentu() 136 {137 this.lvReturn.Items.Clear();138 foreach (Vehicle item in rentVehicles.Values)139 {140 ListViewItem lvitem = new ListViewItem(item.LiceseNo);141 if (item is Car)142 {143 lvitem.SubItems.Add(item.Name);144 lvitem.SubItems.Add(item.Color);145 lvitem.SubItems.Add(item.YearsOfService.ToString());146 lvitem.SubItems.Add(item.DailyRent.ToString());147 148 }149 if (item is Truck)150 {151 lvitem.SubItems.Add(item.Name);152 lvitem.SubItems.Add(item.Color);153 lvitem.SubItems.Add(item.YearsOfService.ToString());154 lvitem.SubItems.Add(item.DailyRent.ToString());155 lvitem.SubItems.Add(((Truck)item).Load.ToString());156 }157 158 this.lvReturn.Items.Add(lvitem);159 }160 }161 private void btnQueryReturn_Click(object sender, EventArgs e)162 {163 164 UpdateLvRentu();165 }166 167 private void btnCompute_Click(object sender, EventArgs e)168 {169 if (this.lvReturn.SelectedItems.Count > 0)//必须有选中的车辆170 {171 if (this.txtRentDate.Text==string.Empty)//租车天数不能为空172 {173 MessageBox.Show("请输入租车天数!!");174 return;175 }176 rentVehicles[this.lvReturn.SelectedItems[0].SubItems[0].Text].RentDate = Convert.ToInt32(this.txtRentDate.Text);177 double price = rentVehicles[this.lvReturn.SelectedItems[0].SubItems[0].Text].CalPrice();178 MessageBox.Show("您的总价是" + price.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);//显示租车金额179 if (rentVehicles.ContainsKey(this.lvReturn.SelectedItems[0].SubItems[0].Text))//判断租出车辆信息集合中是否存在还车的车辆180 {181 182 vehicles.Add(rentVehicles[this.lvReturn.SelectedItems[0].SubItems[0].Text].LiceseNo, rentVehicles[this.lvReturn.SelectedItems[0].SubItems[0].Text]);//再度添到可租车辆集合中183 rentVehicles.Remove(this.lvReturn.SelectedItems[0].SubItems[0].Text);//从租出车辆集合中移除184 }185 UpdateLvRentu();//刷新信息186 }187 else 188 {189 MessageBox.Show("请选择一辆车进行归还!!");190 }191 192 }193 194 private void btnAdd_Click(object sender, EventArgs e)195 {196 197 if (this.rdoCar.Checked)//判断哪个按钮选中,添入不同的车辆信息198 {199 200 Car car = new Car(this.cobColor.Text, Convert.ToInt32(this.txtLetting.Text), this.txtAutoNum.Text, this.txtName.Text,Convert.ToInt32(this.txtYears.Text));201 vehicles.Add(this.txtName.Text, car);202 MessageBox.Show("入库成功!!");203 }204 else 205 {206 this.txtLoad.Enabled = true;207 Truck car = new Truck(Convert.ToInt32(this.txtLoad.Text), this.cobColor.Text, Convert.ToInt32(this.txtLetting.Text), this.txtAutoNum.Text, this.txtName.Text, Convert.ToInt32(this.txtYears.Text));208 vehicles.Add(this.txtName.Text, car);209 MessageBox.Show("入库成功!!");210 }211 212 }213 214 private void rdoTruck_CheckedChanged(object sender, EventArgs e)//单选钮点击改变事件,将载重文本框设为可用215 {216 if (this.rdoTruck.Checked)217 {218 this.txtLoad.Enabled = true;219 220 }221 else222 {223 this.txtLoad.Enabled = false;224 }225 }226 }