多态vob

多态的概念

说人话就是“多种状态”

让继承同一父类的子类们,在执行相同方法是有不同的表现(状态)

主要目的

同一父类的对象执行相同的方法,会有不同的表现。因此这样可以让同一的对象有唯一行为的特征

解决的问题

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
using System;
namespace vob
{
class Father
{
public void SpeakName()
{
Console.WriteLine("Father的方法");
}
}
class Son:Father
{
public new void SpeakName()//覆盖父类的方法
{
Console.WriteLine("Son的方法");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("");
Father f = new Son();
f.SpeakName();//执行的是father的方法,
//因为是father类的,需要转换成son类,才会是son的方法
(f as Son).SpeakName();//打印son的方法
}
}
}

多态的实现

看到这里的小伙伴可能好奇,vob是什么意思呢?vob是三个名词的缩写即

  • v(virtual)—-虚函数
  • o(override)—-重写
  • b(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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
namespace vob
{
class GameObject
{
public string name;
public GameObject(string name)
{
this.name = name;
}
//虚函数 可以被子类重写 vob的第一个字
public virtual void Atk()
{
Console.WriteLine("游戏对象进行攻击");
}
}
class Player:GameObject
{
public Player(string name):base(name)
{

}
//虚函数的重写 vob的第二个字
//重写父类的行为方法
public override void Atk()
{
//base的作用 vob的第三个字
//代表父类 可以通过base来保留父类的行为
base.Atk();//执行父类的行为方法
//修改父类的行为方法
Console.WriteLine("玩家的攻击");
}
}
class Monster : GameObject
{
public Monster(string name) : base(name)
{

}
public override void Atk()
{
Console.WriteLine("怪物的攻击");
}

}
class Program
{
static void Main(string[] args)
{
GameObject p = new Player("铅笔沫");
p.Atk();
(p as Player).Atk();//执行结果与上方一致
GameObject m = new Monster("怪物");
m.Atk();
(m as Monster).Atk();//结果执行与上方一致
}
}
}

练习题

Q1:

真的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子唧唧叫

Q2:

所有员工9点打卡,但是经理十一点,程序员不打卡

Q3:

创建一个图形类,有求面积和周长两个方法,创建矩形类,正方形类,圆形类,继承图形类,实例化矩形、正方形、圆形对象求面积和周长

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
using System;
namespace homework
{
class Duck
{
public virtual void Speak()
{
Console.WriteLine("真的鸭子嘎嘎叫");
}
}
class Wood : Duck
{
public override void Speak()
{
Console.WriteLine("木头鸭子吱吱叫");
}
}
class Rubber : Duck
{
public override void Speak()
{
Console.WriteLine("橡胶鸭子唧唧叫");
}
}
class Program
{
public static void Main(string[] args)
{
Duck d = new Duck();
d.Speak();
Duck d2 = new Wood();
d2.Speak();
Duck d3 = new Rubber();
d3.Speak();
}
}
}

A2:

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
using System;
namespace homework
{
class Staff
{
public virtual void PunchTheClock()
{
Console.WriteLine("9点打卡");
}
}
class Manager : Staff
{
public override void PunchTheClock()
{
Console.WriteLine("11点打卡");
}
}
class Programmer : Staff
{
public override void PunchTheClock()
{
Console.WriteLine("老子不打卡");
}
}
class Program
{
public static void Main(string[] args)
{
Staff staff = new Staff();
staff.PunchTheClock();
Manager manager = new Manager();
manager.PunchTheClock();
Programmer programmer = new Programmer();
programmer.PunchTheClock();
}
}
}

A3:

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
using System;
namespace homework
{
class Graph
{
public virtual float GetLength()
{
return 0;
}
public virtual float GetArea()
{
return 0;
}
}
class Rect:Graph
{
private float w;
private float h;
public Rect(float w, float h)
{
this.w = w;
this.h = h;
}
public override float GetLength()
{
return 2*(w+h);
}
public override float GetArea()
{
return w * h;
}
}
class Square:Graph
{
private float l;
public Square(float l)
{
this.l = l;
}

public override float GetLength()
{
return 4*l;
}
public override float GetArea()
{
return l*l;
}
}
class Circular:Graph
{
private float r;
public Circular(float r)
{
this.r = r;
}

public override float GetLength()
{
return 2*3.14f*r;
}
public override float GetArea()
{
return 3.14f*r*r;
}
}
class Program
{
public static void Main(string[] args)
{
Rect rect = new Rect(2,3);
Console.WriteLine(rect.GetLength());
Console.WriteLine(rect.GetArea());
Square square = new Spuare(3);
Console.WriteLine(square.GetLength());
Console.WriteLine(square.GetArea());
Circular circular = new Circular(3);
Console.WriteLine(circular.GetLength());
Console.WriteLine(circular.GetArea());
}
}
}

抽象类和抽象方法

抽象类

基本概念

就是被抽象关键字abstract修饰的类

特点

  1. 不能被实例化的类
  2. 可以包含抽象方法
  3. 继承抽象类必须重写其抽象方法
  4. 抽象类的子类可以通过里氏替换原则,进行实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
namespace homework
{
abstract class Thing
{
//抽象类中,封装的所有知识点都可以在其中书写
public string name;
//还可以在抽象类中写抽象基函数
}
class Water : Thing
{

}
class Program
{
public static void Main(string[] args)
{
//抽象类不能实例化
//Thing thing = new Thing();//无法实例化抽象类
//但是可以遵循里氏替换原则,用父类容器承装子类
Thing w = new Water();
}
}
}

抽象函数

基本概念

抽象函数,又称纯虚方法,用abstract关键字修饰的方法

特点

只能在抽象类中申明

没有函数(方法)体,只能定义(看不懂的话直接看下方代码)

不能是私有的

继承后必须使用override重写

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
 using System;
namespace homework
{
abstract class Fruits
{
public string name;
//抽象方法是一定不能有函数(方法)体的
public abstract void Bad();
public virtual void Test()
{

}
}
class Apple:Fruits
{
//在继承抽象类时,如果不重写其中的抽象函数,就会报错
public override void Bad()
{
}
public override void Test()
{

}
}
class Banana:Fruits
{
//由此可以看到,虚方法和抽象方法,都可以被子类无限重写
public override void Bad()
{
}
public override void Test()
{
}
}
class Program
{
public static void Main(string[] args)
{

}
}
}

看到这里的小伙伴,可能会想,这两个给东西有什么用呢?

是这样的,当我们不希望被实例化的对象,相对比较抽象的类可以使用抽象类例如(人 事物 水果 …),以及当父类中的行为,不太需要被是实现,只希望子类去定义具体规则的,可以选择使用抽象类,然后使用其中的方法来定义规则。

具体一点 当时设计整体框架时,就是它们大显身手的时候

练习题

Q1

写一个动物抽象类,写三个子类,分别为(人叫,狗叫,猫叫)

Q2

创建一个图形类,有求面积和周长两个方法,创建矩形类、正方形类、圆形类,继承图形类,实例化矩形、正方形、圆形对象求面积和周长

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
using System;
namespace homework
{
abstract class Animals
{
public abstract void Talk();
}
class Person:Animals
{
public override void Talk()
{
Console.WriteLine("人在叫");
}
}
class Cats : Animals
{
public override void Talk()
{
Console.WriteLine("猫在叫");
}
}
class Dogs : Animals
{
public override void Talk()
{
Console.WriteLine("狗在叫");
}
}
class Program
{
public static void Main(string[] args)
{
Animals p = new Person();
p.Talk();
Animals d = new Dogs();
d.Talk();
Animals c = new Cats();
c.Talk();
}
}
}

A2

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
using System;
namespace homework
{
abstract class Graph
{
public abstract float GetLength();
public abstract float GetArea();
}
class Rect : Graph
{
private float w;
private float h;
public Rect(float w, float h)
{
this.w = w;
this.h = h;
}
public override float GetLength()
{
return 2 * (w + h);
}
public override float GetArea()
{
return w * h;
}
}
class Square : Graph
{
private float l;
public Square(float l)
{
this.l = l;
}

public override float GetLength()
{
return 4 * l;
}
public override float GetArea()
{
return l * l;
}
}
class Circular : Graph
{
private float r;
public Circular(float r)
{
this.r = r;
}

public override float GetLength()
{
return 2 * 3.14f * r;
}
public override float GetArea()
{
return 3.14f * r * r;
}
}
class Program
{
public static void Main(string[] args)
{
Graph rect = new Rect(2, 3);
Console.WriteLine(rect.GetLength());
Console.WriteLine(rect.GetArea());
Graph square = new Square(3);
Console.WriteLine(square.GetLength());
Console.WriteLine(square.GetArea());
Graph circular = new Circular(3);
Console.WriteLine(circular.GetLength());
Console.WriteLine(circular.GetArea());
}
}
}

接口

你可以把接口理解为是一种DLC,在原有的抽象类的基础上,增加一些特定的行为。例如,人与鸟,都属于动物,但是人不会飞,因此飞这个行为们就不适合写在动物恶的这个抽象类当中,所以可以通过接口,来给鸟类,来添加一种拓展的行为方式。并且这么写的好处还有,飞机类也可以使用飞的行为方式。因为接口可以被任何一个类继承。

基本概念

接口是行为的抽象规范,它也是一种自定义类型,关键字:interface

接口申明的规范

  1. 包含成员变量
  2. 包含方法、属性、索引器、事件
  3. 成员不能被实现
  4. 成员可以不用写访问修饰符号(默认public),但不能是私有
  5. 接口不能继承类,但是可以继承另一个接口

接口的使用规范

  1. 类可以继承多个接口
  2. 类继承接口后,必须实现接口中所有成员

特点

  1. 和类的申明类似
  2. 接口时用来继承的
  3. 接口不能被实例化,但是可以作为容器的存储对象(遵循里氏替换原则)
1
2
3
4
5
6
7
class Program
{
static void Main(string[] args)
{
xxx f = new xxx();//报错,因为接口不能被实例化
}
}

申明方法

基本语法

1
2
3
4
interface 接口名
{

}

帮助记忆:接口时抽象行为的“基类”

命名规范:帕斯卡命名前加I!!!!!!!

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
using System;
namespace homework
{
interface IFly
{
//方法不能有语句块
void Fly();
//属性只能用自动属性去写
string Name
{
get;
set;
}
//索引器里没有语句
int this[int index]
{
get;
set;
}
//事件后续再提到
event Action dosomthing;
}
class Program
{
public static void Main(string[] args)
{
}
}
}

使用方法

接口可以用来继承

1.一个类可以继承n个接口

1
2
3
4
class Person,xxx,xxxx//xxx,xxxx为接口名称
{

}

2.继承了接口后就必须实现其中的内容,并且必须是public的

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
class Animal
{

}
//同时继承一个类,再继承一个接口
class Person:Animal,IFly
{
public void Fly()
{

}
public string Name
{
get;
set;
}
public int this[int index]
{
get
{
return 0;
}
set
{

}
}
public event Action dosomthing;
}

3.实现的接口函数可以加v(virtual)再向子类中重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person:Animal,IFly
{
public virtual void Fly()
{
//利用虚函数重写
}
public string Name
{
get;
set;
}
public int this[int index]
{
get
{
return 0;
}
set
{

}
}
public event Action dosomthing;
}

4.接口也遵循里氏替换原则

1
2
3
4
5
6
7
class Program
{
static void Main(string[] args)
{
IFly f = new Person();
}
}

继承方式

接口是可以继承接口的

并且当接口继承接口时,不需要实现,而是通过后续的类继承时,再去实现所有接口的内容

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
using System;
namespace homework
{
interface IFly
{
//方法不能有语句块
void Fly();
//属性只能用自动属性去写
string Name
{
get;
set;
}
//索引器里没有语句
int this[int index]
{
get;
set;
}
//事件后续再提到
event Action dosomthing;
}
interface IWalk
{
void Walk();
}
interface IMove:IFly,IWalk
{
void Move();

}
class Tset : IMove
{
//暂时不要在意这里面的东西,只要知道是用来给实现占坑的就可以了
public int this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public string Name { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public event Action dosomthing;

public void Fly()
{
throw new NotImplementedException();
}

public void Move()
{
throw new NotImplementedException();
}

public void Walk()
{
throw new NotImplementedException();
}
}
class Program
{
public static void Main(string[] args)
{
IMove im = new Tset();
IFly ifly = new Tset();
IWalk iw = new Tset();
}
}
}

有的小伙伴可能会疑问throw new NotImplementedException()是什么意思呢?这是VS中给类快速重构所有元素的一个方式,因为当一个子类的父类是抽象类,或者含有虚函数,抽象函数等,需要在类中重新书写的元素,再不定义之前,会进行报错。因此可以使用快速重构的方式,通过使用throw new NotImplementedException()来进行占位,意思是提醒你在这个位置,你还没有进行重写。

显式实现接口

这是一个使用性并不是很强的东西,但面试的时候可能会需要。

当一个类继承两个接口时,接口中有同名重复的时候,所使用的一个方法。

值得注意的是:显示实现接口时,不能写访问修饰符

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
using System;
namespace homework
{
interface IAtk
{
void Atk();
}
interface ISuperAtk
{
void Atk();
}
class Player : IAtk, ISuperAtk
{
//失去原本的两种不同行为方式的意义
//后面的调用都只会使用的是同一种行为表示
//public void Atk()
//{

//}
void IAtk.Atk()
{
throw new NotImplementedException();
}

void ISuperAtk.Atk()
{
throw new NotImplementedException();
}
}
class Program
{
public static void Main(string[] args)
{
IAtk ia = new Player();
ia.Atk();
ISuperAtk isa = new Player();
isa.Atk();
//调用方法
Player p =new Player();
(p as IAtk).Atk();
(p as ISuperAtk).Atk();
}
}
}

练习题

Q1

人、汽车、房子都需要登记,人需要到派出所登记,汽车需要去车管所登记,房子需要去房管局登记
使用接口实现登记方法

Q2

麻雀、鸵鸟、企鹅、鹦鹉、直升机、天鹅。直升机和部分鸟能飞
鸵鸟和企鹅不能飞企鹅和天鹅能游泳
除直升机,其它都能走
请用面向对象相关知识实现

Q3

多态来模拟移动硬盘、U盘、MP3查到电脑上读取数据移动硬盘与U盘都属于存储设备
MP3属于播放设备
但他们都能插在电脑上传输数据电脑提供了一个USB接口
请实现电脑的传输数据的功能

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
using System;
using System.Diagnostics;

namespace homework
{
interface IRegister
{
void Register();
}
class Person:IRegister
{
public void Register()
{
Console.WriteLine("在派出所登记");
}
}
class Car:IRegister
{
public void Register()
{
Console.WriteLine("在车管所登记");
}
}
class House:IRegister
{
public void Register()
{
Console.WriteLine("在房管局登记");
}
}
class Program
{
public static void Main(string[] args)
{
IRegister[] arr = new IRegister[] { new Person(), new Home(), new Car() };
for (int i = 0; i < arr.Length; i++)
{
arr[i].Register();
}
}
}
}

A2

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.Diagnostics;

namespace homework
{
abstract class Bird
{
public abstract void Walk();
}
interface IFly
{
void Fly();
}
interface ISwimming
{
void Swimming();
}
class Sparrow : Bird, IFly
{
public void Fly()
{

}

public override void Walk()
{

}
}

class Ostrich : Bird
{
public override void Walk()
{

}
}

class Penguin : Bird, ISwimming
{
public void Swimming()
{

}

public override void Walk()
{

}
}

class Parrot : Bird, IFly
{
public void Fly()
{

}

public override void Walk()
{

}
}

class Swan : Bird, IFly, ISwimming
{
public void Fly()
{

}

public void Swimming()
{

}

public override void Walk()
{

}
}

class Helicopter : IFly
{
public void Fly()
{

}
}
class Program
{
public static void Main(string[] args)
{
}
}
}

A3

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
using System;
using System.Diagnostics;

namespace homework
{
interface IUSB
{
void ReadData();
}
class StorageDevice : IUSB
{
public string name;
public StorageDevice(string name)
{
this.name = name;
}
public void ReadData()
{
Console.WriteLine("{0}正在传输数据", name);
}
}
class MP3 : IUSB
{
public void ReadData()
{
Console.WriteLine("MP3传输数据");
}
}
class Computer
{
public IUSB usb1;
}
class Program
{
public static void Main(string[] args)
{
StorageDevice hd = new StorageDevice("移动硬盘");
StorageDevice ud = new StorageDevice("U盘");
MP3 mp3 = new MP3();

Computer c = new Computer();

c.usb1 = hd;
c.usb1.ReadData();

c.usb1 = ud;
c.usb1.ReadData();

c.usb1 = mp3;
c.usb1.ReadData();
}
}
}