继承的基本规则 基本概念 继承的基本概念 就是,一个类A继承一个类B,类A将会继承类B的所有成员和所有的特征和行为
被继承的类:父类,基类
继承的类:子类,派生类
子类可以有自己的 特征和行为
特点: 1.单根性 子类只能有一个父类
注意:C#中没有C++的多继承!!!
2.传递性 子类可以简介继承父类的父类
基本语法 C#的继承和C++的类似,都是通过使用:
来实现。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 using System;using System.Runtime.InteropServices;namespace Homework { class Teacher { public string name; public int number; public void SpeakName () { Console.WriteLine(name); } } class TeachingTeacher : Teacher { public string subject; public void SpeakSubject () { Console.WriteLine(subject+"老师" ); } } class ChineseTeacher : TeachingTeacher { public void Skill () { Console.WriteLine("我是语文老师" ); } } class Program { static void Main (string [] args ) { TeachingTeacher tt = new TeachingTeacher(); tt.name = "铅笔沫" ; tt.number = 114514 ; tt.SpeakName(); tt.subject = "Csharp" ; tt.SpeakSubject(); ChineseTeacher ct = new ChineseTeacher(); ct.name = "云笙繁华" ; ct.number = 191981 ; ct.subject = "语文" ; ct.SpeakName(); ct.SpeakSubject(); ct.Skill(); } } }
访问修饰符的影响
类型
可访问对象
public
公共
内外部都可访问
private
私有
内部访问
protected
保护
内部和子类访问
internal(暂时了解)
内部的
在同一程序集的内部和成员
子类和父类的同名成员 C#中允许子类存在和父类同名的成员(默认使用子类)
但是 及其不建议这么做,因此也不在这里过多叙述。
解决方式
1 2 3 - public string name;(默认覆盖) + public new string name;(手写覆盖)
练习题 Q1 写一个人类,人类中有姓名,年龄属性,有说话行为,战士类继承人类,有攻击行为
A1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using System;using System.Runtime.InteropServices;namespace Homework { class Person { public string name; public int age; public void Speak () { Console.WriteLine("我是" + name); } } class Soldier : Person { public void Attack () { Console.WriteLine("冲啊" ); } } class Program { static void Main (string [] args ) { Soldier s = new Soldier(); s.name = "铅笔沫" ; s.Speak(); s.Attack(); } } }
里氏替换原则 面向对象七大原则之一,最容易理解且最重要的原则
概念 任何父类出现的地方,子类都可以替代
重点 语法表现——父类容器装子类对象,因为子类对象中包含了父类的所有内容
作用 方便进行对象存储和管理
基本实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 using System;using System.Runtime.InteropServices;namespace Homework { class GameObject { } class Player : GameObject { public void PlayerAtk () { Console.WriteLine("玩家攻击" ); } } class Monster : GameObject { public void MonsterAtk () { Console.WriteLine("怪物攻击" ); } } class Boss : GameObject { public void BossAtk () { Console.WriteLine("Boss攻击" ); } } class Program { static void Main (string [] args ) { GameObject player = new Player(); GameObject monster = new Monster(); GameObject boss = new Boss(); GameObject[] objects=new GameObject[] {new Player(), new Monster(), new Boss()}; } } }
is 和 as 基本概念
基本概念
返回值
结果
is
判断一个对象是否为执行对象
布尔类型
是为真,不是为假
as
将一个对象转换为指定类对象
指定类型对象
成功,返回指定类型对象,失败返回Null
基本语法
1 2 3 4 5 6 7 if (player is Player){ Player p = player as Player; p.PlayerAtk(); (player as Player).PlayerAtk(); }
当父类数组里的内容不知道顺序是,可以使用for来遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 for (int i=0 ;i<objects.Lenth;i++){ if (objects[i] is Player) { (objects[i] as Player).PlayerAtk(); } else if (objects[i] is Monster) { (objects[i] as Monster).MonsterAtk(); } else if (objects[i] is Boss) { (objects[i] as Boss).BossAtk(); } }
练习题 Q1:is 和 as 的区别是什么 写一个Monster类,它派生出Boss和Goblin两个类,Boss有技能; 小有攻击;随机生成10个怪,装载到数组中,遍历这个数组,调用他们的攻击方法,如果是boss就释放技能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;namespace Homework { class Monster { } class Boss : Monster { public void BossSkill () { Console.WriteLine("Boss发动技能" ); } } class Goblin : Monster { public void GoblinAtk () { Console.WriteLine("哥布林攻击" ); } } class Program { static void Main (string [] args ) { Random r = new Random(); int randomNum; Monster[] monsters = new Monster[10 ]; for (int i= 0 ; i < monsters.Length;i++) { randomNum = r.Next(1 ,101 ); if (randomNum < 50 ) { monsters[i] = new Boss(); } else { monsters[i] = new Goblin(); } } for (int i=0 ;i<monsters.Length;i++) { if (monsters[i] is Boss) { (monsters[i] as Boss).BossSkill(); Console.WriteLine("发动技能成功" ); } else if (monsters[i] is Goblin) { (monsters[i] as Goblin).GoblinAtk(); } } } } }
Q2:Fps游戏模拟 写一个玩家类,玩家可以拥有各种武器现在有四种武器,冲锋枪,散弹枪,手枪,匕首玩家默认拥有匕首 请在玩家类中写一个方法,可以拾取不同的武器替换自己拥有的枪械
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;namespace Homework { class Gamer { private Weapon nowHaveWeapon; public Gamer () { nowHaveWeapon = new Dagger(); } public void PickUp (Weapon weapon ) { nowHaveWeapon = weapon; } } class Weapon { } class SubmachineGun : Weapon { } class ShotGun : Weapon { } class Pistol :Weapon { } class Dagger : Weapon { } class Program { static void Main (string [] args ) { Gamer p = new Gamer(); SubmachineGun s = new SubmachineGun(); p.PickUp(s); ShotGun sg = new ShotGun(); p.PickUp(sg); } } }
继承中的构造函数 基本概念 当申明一个子类对象时,先执行父类的构造函数,再执行子类的构造函数
注意
父类的无参构造,很重要
子类可以通过base
关键字调用父类构造。
执行顺序 父类的父类构造->父类构造->子类构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;namespace Homework { class GameObject { public GameObject () { Console.WriteLine("GameObject的构造函数" ); } } class Player : GameObject { public Player () { Console.WriteLine("Player的构造函数" ); } } class MainPlayer :Player { public MainPlayer () { Console.WriteLine("MainPlayer的构造函数" ); } } class Program { static void Main (string [] args ) { MainPlayer player = new MainPlayer(); } } }
父类的无参构造函数重要 由于子类默认调用的是父类的无参构造函数,因此当父类有参构造函数,并且没有将无参构造空实现时,将无法正常构建子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Father { public Father () { } public Father (int i ) { Console.WriteLine("Father构造" ); } } class Son : Father { public Son () { Console.WriteLine("Son的构造" ); } }
通过base调用指定父类函数 要是想要子类调用一个指定的构造函数(无参或者有参)该怎么办呢?
那我们可以选择通过base来进行调用,并且也可以解决 刚才由于父类中没有无参构造,导致子类无法继承的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;namespace Homework { class Father { public Father (int i ) { Console.WriteLine("Father构造" ); } } class Son : Father { public Son (int i ) : base (i ) { Console.WriteLine("Son的第一个构造" ); } public Son (int i, string str ) : this (i ) { Console.WriteLine("Son的第二个构造" ); } } class Program { static void Main (string [] args ) { Son son = new Son(1 , "123" ); } } }
换而言之,base调用的是父类中的构造函数,而this只能调用属于自己的构造函数。
练习题 Q1: 有一个打工人基类,有工种,工作内容两个特征,一个工作方法。程序员、策划、美术,分别继承打工人,请用继承中的构造函数这个知识点,实例化3个上述对象。
A1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;using System.Security.Cryptography.X509Certificates;namespace Homework { class Worker { public string type; public string content; public Worker (string type, string content ) { this .type = type; this .content = content; } public void Work () { Console.WriteLine("{0} {1}" ,type ,content); } } class Programmer :Worker { public Programmer () : base ("程序员" , "编程" ) { } } class Plan :Worker { public Plan ():base ("策划" ,"设计游戏" ) { } } class Art : Worker { public Art () : base ("美术" , "画画" ) { } } class Program { static void Main (string [] args ) { Programmer programmer = new Programmer(); programmer.Work(); Plan plan= new Plan(); plan.Work(); Art art= new Art(); art .Work(); } } }
万物之父和装箱拆箱 万物之父 关键字:object
或Object
概念 object是所有类型的基类 ,它是一个类(引用类型)
作用
可以利用里氏替换原则,用object容器装所有对象
可以用来表示不确定类型,作为函数参数类型
万物之父的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Father { } class Son : Father { public void Speak () { } } class Program { static void Main (string [] args ) { Console.WriteLine("万物之父和装箱拆箱" ); Father f = new Son(); if ( f is Son ) { (f as Son).Speak(); } object o = new Son(); if ( o is Son ) { (o as Son).Speak(); } object o2 = 1f ; float fl = (float )o2; o = Son(o) object str = "123123" ; string str2 = str as string ; object arr = new int [10 ]; int [] ar = arr as int []; } }
装箱拆箱 装箱和拆箱,大可以理解为一种行为 。
发生条件 用object存储 值类型时,称为装箱 。再将object转为 值类型时,称为拆箱
装箱 把值类型用引用类型存储,栈内存会迁移到堆内存中。因为有内存的迁移,就会带来性能的消耗。
拆箱 把引用类型存储的值类型取出来,堆内存会迁移到栈内存中。
因此,要尽量少用装箱和拆箱,这两个功能。
好处 不确定类型时可以方便参数的存储和传递
坏处 存在内存迁移,增加性能消耗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Program { static void Main (string [] args ) { object v = 3 ; int intV = (int )v; TestFun(1 ,2 ,3 ,4 ,5 ,6 ); TestFun_2(1 ,2 ,3f ,"456" ) } static void TsetFun (params int [] array ) { } static void TsetFun_2 (params object [] array ) { } }
密封类 基本概念 是一个使用 sealed密封关键字修饰类,使该类无法再被继承
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Father { } sealed class Son :Father { }
作用 在面向对象程序的设计中,密封类的主要作用就是不允许最底层子类被继承,可以保证程序的规范性、安全性。
练习题 Q1: 定义一个载具类,速度,最大速度,可乘人数,司机和乘客等,有上车,下车,行驶,车祸等方法,用载具声明一个对象,并将若干人装载上车。
A1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 using System;using System.Runtime.ConstrainedExecution;using System.Runtime.InteropServices;using System.Security.Cryptography.X509Certificates;namespace Homework { class Person { } class Driver :Person { } class Passenger : Person { } class Car { public int speed; public int maxspeed; public int num; public Person[] persons; public Car (int speed, int maxspeed, int num ) { this .speed = speed; this .maxspeed = maxspeed; this .num = 0 ; persons= new Person[num]; } public void GetIn (Person p ) { if (num>=persons.Length) { Console.WriteLine("满载" ); return ; } persons[num] = p; ++num; } public void GetOff (Person p ) { for (int i=0 ;i<persons.Length;i++) { if (persons[i] == null ) { break ; } if (persons[i]==p) { for (int j=i;j<num-1 ;j++) { persons[j] = persons[j+1 ]; } persons[num - 1 ] = null ; --num; Console.WriteLine("我下车了" ); break ; } } } public void Move () { } public void Boom () { } } class Program { static void Main (string [] args ) { Car c = new Car(10 , 20 , 20 ); Driver d = new Driver(); c.GetIn(d); Passenger p =new Passenger(); c.GetIn(p); Passenger p2 =new Passenger(); c.GetIn(p2); Passenger p3 = new Passenger(); c.GetIn(p3); c.GetOff(p2); } } }